Commit edb210905d

Michael Dusan <michael.dusan@gmail.com>
2020-02-11 03:08:08
stage1: memory/report overhaul
- split util_base.hpp from util.hpp - new namespaces: `mem` and `heap` - new `mem::Allocator` interface - new `heap::CAllocator` impl with global `heap::c_allocator` - new `heap::ArenaAllocator` impl - new `mem::TypeInfo` extracts names without RTTI - name extraction is enabled w/ ZIG_ENABLE_MEM_PROFILE=1 - new `mem::List` takes explicit `Allocator&` parameter - new `mem::HashMap` takes explicit `Allocator&` parameter - add Codegen.pass1_arena and use for all `ZigValue` allocs - deinit Codegen.pass1_arena early in `zig_llvm_emit_output()`
1 parent 1cdefeb
src/all_types.hpp
@@ -2000,6 +2000,9 @@ struct CFile {
 
 // When adding fields, check if they should be added to the hash computation in build_with_cache
 struct CodeGen {
+    // arena allocator destroyed just prior to codegen emit
+    heap::ArenaAllocator *pass1_arena;
+
     //////////////////////////// Runtime State
     LLVMModuleRef module;
     ZigList<ErrorMsg*> errors;
@@ -2280,7 +2283,6 @@ struct ZigVar {
     Scope *parent_scope;
     Scope *child_scope;
     LLVMValueRef param_value_ref;
-    IrExecutableSrc *owner_exec;
 
     Buf *section_name;
 
src/analyze.cpp
@@ -80,7 +80,7 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node,
 }
 
 ZigType *new_type_table_entry(ZigTypeId id) {
-    ZigType *entry = allocate<ZigType>(1);
+    ZigType *entry = heap::c_allocator.create<ZigType>();
     entry->id = id;
     return entry;
 }
@@ -140,7 +140,7 @@ void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope
 static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
         ZigType *import, Buf *bare_name)
 {
-    ScopeDecls *scope = allocate<ScopeDecls>(1);
+    ScopeDecls *scope = heap::c_allocator.create<ScopeDecls>();
     init_scope(g, &scope->base, ScopeIdDecls, node, parent);
     scope->decl_table.init(4);
     scope->container_type = container_type;
@@ -151,7 +151,7 @@ static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent,
 
 ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeBlock);
-    ScopeBlock *scope = allocate<ScopeBlock>(1);
+    ScopeBlock *scope = heap::c_allocator.create<ScopeBlock>();
     init_scope(g, &scope->base, ScopeIdBlock, node, parent);
     scope->name = node->data.block.name;
     return scope;
@@ -159,20 +159,20 @@ ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
 
 ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeDefer);
-    ScopeDefer *scope = allocate<ScopeDefer>(1);
+    ScopeDefer *scope = heap::c_allocator.create<ScopeDefer>();
     init_scope(g, &scope->base, ScopeIdDefer, node, parent);
     return scope;
 }
 
 ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeDefer);
-    ScopeDeferExpr *scope = allocate<ScopeDeferExpr>(1);
+    ScopeDeferExpr *scope = heap::c_allocator.create<ScopeDeferExpr>();
     init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
     return scope;
 }
 
 Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
-    ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
+    ScopeVarDecl *scope = heap::c_allocator.create<ScopeVarDecl>();
     init_scope(g, &scope->base, ScopeIdVarDecl, node, parent);
     scope->var = var;
     return &scope->base;
@@ -180,14 +180,14 @@ Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
 
 ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeFnCallExpr);
-    ScopeCImport *scope = allocate<ScopeCImport>(1);
+    ScopeCImport *scope = heap::c_allocator.create<ScopeCImport>();
     init_scope(g, &scope->base, ScopeIdCImport, node, parent);
     buf_resize(&scope->buf, 0);
     return scope;
 }
 
 ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
-    ScopeLoop *scope = allocate<ScopeLoop>(1);
+    ScopeLoop *scope = heap::c_allocator.create<ScopeLoop>();
     init_scope(g, &scope->base, ScopeIdLoop, node, parent);
     if (node->type == NodeTypeWhileExpr) {
         scope->name = node->data.while_expr.name;
@@ -200,7 +200,7 @@ ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
 }
 
 Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime) {
-    ScopeRuntime *scope = allocate<ScopeRuntime>(1);
+    ScopeRuntime *scope = heap::c_allocator.create<ScopeRuntime>();
     scope->is_comptime = is_comptime;
     init_scope(g, &scope->base, ScopeIdRuntime, node, parent);
     return &scope->base;
@@ -208,37 +208,37 @@ Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc
 
 ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeSuspend);
-    ScopeSuspend *scope = allocate<ScopeSuspend>(1);
+    ScopeSuspend *scope = heap::c_allocator.create<ScopeSuspend>();
     init_scope(g, &scope->base, ScopeIdSuspend, node, parent);
     return scope;
 }
 
 ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) {
-    ScopeFnDef *scope = allocate<ScopeFnDef>(1);
+    ScopeFnDef *scope = heap::c_allocator.create<ScopeFnDef>();
     init_scope(g, &scope->base, ScopeIdFnDef, node, parent);
     scope->fn_entry = fn_entry;
     return scope;
 }
 
 Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
-    ScopeCompTime *scope = allocate<ScopeCompTime>(1);
+    ScopeCompTime *scope = heap::c_allocator.create<ScopeCompTime>();
     init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
     return &scope->base;
 }
 
 Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
-    ScopeTypeOf *scope = allocate<ScopeTypeOf>(1);
+    ScopeTypeOf *scope = heap::c_allocator.create<ScopeTypeOf>();
     init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
     return &scope->base;
 }
 
 ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
-    ScopeExpr *scope = allocate<ScopeExpr>(1);
+    ScopeExpr *scope = heap::c_allocator.create<ScopeExpr>();
     init_scope(g, &scope->base, ScopeIdExpr, node, parent);
     ScopeExpr *parent_expr = find_expr_scope(parent);
     if (parent_expr != nullptr) {
         size_t new_len = parent_expr->children_len + 1;
-        parent_expr->children_ptr = reallocate_nonzero<ScopeExpr *>(
+        parent_expr->children_ptr = heap::c_allocator.reallocate_nonzero<ScopeExpr *>(
                 parent_expr->children_ptr, parent_expr->children_len, new_len);
         parent_expr->children_ptr[parent_expr->children_len] = scope;
         parent_expr->children_len = new_len;
@@ -1104,8 +1104,8 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
 {
     Error err;
 
-    ZigValue *result = create_const_vals(1);
-    ZigValue *result_ptr = create_const_vals(1);
+    ZigValue *result = g->pass1_arena->create<ZigValue>();
+    ZigValue *result_ptr = g->pass1_arena->create<ZigValue>();
     result->special = ConstValSpecialUndef;
     result->type = (type_entry == nullptr) ? g->builtin_types.entry_var : type_entry;
     result_ptr->special = ConstValSpecialStatic;
@@ -1122,7 +1122,6 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
     {
         return g->invalid_inst_gen->value;
     }
-    destroy(result_ptr, "ZigValue");
     return result;
 }
 
@@ -1507,7 +1506,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConventio
 
     fn_type_id->cc = cc;
     fn_type_id->param_count = fn_proto->params.length;
-    fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
+    fn_type_id->param_info = heap::c_allocator.allocate<FnTypeParamInfo>(param_count_alloc);
     fn_type_id->next_param_index = 0;
     fn_type_id->is_var_args = fn_proto->is_var_args;
 }
@@ -2171,7 +2170,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
     bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
     struct_type->data.structure.resolve_loop_flag_other = true;
 
-    uint32_t *host_int_bytes = packed ? allocate<uint32_t>(struct_type->data.structure.gen_field_count) : nullptr;
+    uint32_t *host_int_bytes = packed ? heap::c_allocator.allocate<uint32_t>(struct_type->data.structure.gen_field_count) : nullptr;
 
     size_t packed_bits_offset = 0;
     size_t next_offset = 0;
@@ -2657,7 +2656,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
     }
 
     enum_type->data.enumeration.src_field_count = field_count;
-    enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+    enum_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);
     enum_type->data.enumeration.fields_by_name.init(field_count);
 
     HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
@@ -3034,7 +3033,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
         return ErrorSemanticAnalyzeFail;
     }
     union_type->data.unionation.src_field_count = field_count;
-    union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
+    union_type->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(field_count);
     union_type->data.unionation.fields_by_name.init(field_count);
 
     Scope *scope = &union_type->data.unionation.decls_scope->base;
@@ -3053,7 +3052,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
     if (create_enum_type) {
         occupied_tag_values.init(field_count);
 
-        di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
+        di_enumerators = heap::c_allocator.allocate<ZigLLVMDIEnumerator*>(field_count);
 
         ZigType *tag_int_type;
         if (enum_type_node != nullptr) {
@@ -3086,7 +3085,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
         tag_type->data.enumeration.decl_node = decl_node;
         tag_type->data.enumeration.layout = ContainerLayoutAuto;
         tag_type->data.enumeration.src_field_count = field_count;
-        tag_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+        tag_type->data.enumeration.fields = heap::c_allocator.allocate<TypeEnumField>(field_count);
         tag_type->data.enumeration.fields_by_name.init(field_count);
         tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
     } else if (enum_type_node != nullptr) {
@@ -3106,7 +3105,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
             return err;
         }
         tag_type = enum_type;
-        covered_enum_fields = allocate<bool>(enum_type->data.enumeration.src_field_count);
+        covered_enum_fields = heap::c_allocator.allocate<bool>(enum_type->data.enumeration.src_field_count);
     } else {
         tag_type = nullptr;
     }
@@ -3244,7 +3243,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
             }
             covered_enum_fields[union_field->enum_field->decl_index] = true;
         } else {
-            union_field->enum_field = allocate<TypeEnumField>(1);
+            union_field->enum_field = heap::c_allocator.create<TypeEnumField>();
             union_field->enum_field->name = field_name;
             union_field->enum_field->decl_index = i;
             bigint_init_unsigned(&union_field->enum_field->value, i);
@@ -3366,8 +3365,8 @@ static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool i
 }
 
 ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
-    ZigFn *fn_entry = allocate<ZigFn>(1, "ZigFn");
-    fn_entry->ir_executable = allocate<IrExecutableSrc>(1, "IrExecutableSrc");
+    ZigFn *fn_entry = heap::c_allocator.create<ZigFn>();
+    fn_entry->ir_executable = heap::c_allocator.create<IrExecutableSrc>();
 
     fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
 
@@ -3642,7 +3641,7 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
         return;
     }
 
-    TldFn *tld_fn = allocate<TldFn>(1);
+    TldFn *tld_fn = heap::c_allocator.create<TldFn>();
     init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base);
     g->resolve_queue.append(&tld_fn->base);
 }
@@ -3650,7 +3649,7 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
 static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
     assert(node->type == NodeTypeCompTime);
 
-    TldCompTime *tld_comptime = allocate<TldCompTime>(1);
+    TldCompTime *tld_comptime = heap::c_allocator.create<TldCompTime>();
     init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base);
     g->resolve_queue.append(&tld_comptime->base);
 }
@@ -3673,7 +3672,7 @@ void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) {
     resolve_top_level_decl(g, tld, tld->source_node, false);
     assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk);
     TldVar *tld_var = (TldVar *)tld;
-    copy_const_val(tld_var->var->const_value, value);
+    copy_const_val(g, tld_var->var->const_value, value);
     tld_var->var->var_type = value->type;
     tld_var->var->align_bytes = get_abi_alignment(g, value->type);
 }
@@ -3693,7 +3692,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
             {
                 Buf *name = node->data.variable_declaration.symbol;
                 VisibMod visib_mod = node->data.variable_declaration.visib_mod;
-                TldVar *tld_var = allocate<TldVar>(1);
+                TldVar *tld_var = heap::c_allocator.create<TldVar>();
                 init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
                 tld_var->extern_lib_name = node->data.variable_declaration.lib_name;
                 add_top_level_decl(g, decls_scope, &tld_var->base);
@@ -3709,7 +3708,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
                 }
 
                 VisibMod visib_mod = node->data.fn_proto.visib_mod;
-                TldFn *tld_fn = allocate<TldFn>(1);
+                TldFn *tld_fn = heap::c_allocator.create<TldFn>();
                 init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
                 tld_fn->extern_lib_name = node->data.fn_proto.lib_name;
                 add_top_level_decl(g, decls_scope, &tld_fn->base);
@@ -3718,7 +3717,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
             }
         case NodeTypeUsingNamespace: {
             VisibMod visib_mod = node->data.using_namespace.visib_mod;
-            TldUsingNamespace *tld_using_namespace = allocate<TldUsingNamespace>(1);
+            TldUsingNamespace *tld_using_namespace = heap::c_allocator.create<TldUsingNamespace>();
             init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base);
             add_top_level_decl(g, decls_scope, &tld_using_namespace->base);
             decls_scope->use_decls.append(tld_using_namespace);
@@ -3845,7 +3844,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
     assert(const_value != nullptr);
     assert(var_type != nullptr);
 
-    ZigVar *variable_entry = allocate<ZigVar>(1);
+    ZigVar *variable_entry = heap::c_allocator.create<ZigVar>();
     variable_entry->const_value = const_value;
     variable_entry->var_type = var_type;
     variable_entry->parent_scope = parent_scope;
@@ -3984,7 +3983,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
     ZigType *type = explicit_type ? explicit_type : implicit_type;
     assert(type != nullptr); // should have been caught by the parser
 
-    ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type);
+    ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(g, type);
 
     tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
             is_const, init_val, &tld_var->base, type);
@@ -4491,7 +4490,7 @@ static Error define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) {
         }
 
         ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
-                param_name, true, create_const_runtime(param_type), nullptr, param_type);
+                param_name, true, create_const_runtime(g, param_type), nullptr, param_type);
         var->src_arg_index = i;
         fn_table_entry->child_scope = var->child_scope;
         var->shadowable = var->shadowable || is_var_args;
@@ -4786,7 +4785,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
             } else {
                 return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
                 if (inferred_err_set_type->data.error_set.err_count > 0) {
-                    return_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
+                    return_err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
                     for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
                         return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
                     }
@@ -4919,7 +4918,7 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu
     Buf *bare_name = buf_alloc();
     os_path_extname(src_basename, bare_name, nullptr);
 
-    RootStruct *root_struct = allocate<RootStruct>(1);
+    RootStruct *root_struct = heap::c_allocator.create<RootStruct>();
     root_struct->package = package;
     root_struct->source_code = source_code;
     root_struct->line_offsets = tokenization.line_offsets;
@@ -4946,7 +4945,7 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu
         scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl);
     }
 
-    TldContainer *tld_container = allocate<TldContainer>(1);
+    TldContainer *tld_container = heap::c_allocator.create<TldContainer>();
     init_tld(&tld_container->base, TldIdContainer, namespace_name, VisibModPub, root_node, nullptr);
     tld_container->type_entry = import_entry;
     tld_container->decls_scope = import_entry->data.structure.decls_scope;
@@ -5694,14 +5693,14 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
     if (entry != nullptr) {
         return entry->value;
     }
-    ZigValue *result = create_const_vals(1);
+    ZigValue *result = g->pass1_arena->create<ZigValue>();
     result->type = type_entry;
     result->special = ConstValSpecialStatic;
     if (result->type->id == ZigTypeIdStruct) {
         // The fields array cannot be left unpopulated
         const ZigType *struct_type = result->type;
         const size_t field_count = struct_type->data.structure.src_field_count;
-        result->data.x_struct.fields = alloc_const_vals_ptrs(field_count);
+        result->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
         for (size_t i = 0; i < field_count; i += 1) {
             TypeStructField *field = struct_type->data.structure.fields[i];
             ZigType *field_type = resolve_struct_field_type(g, field);
@@ -5786,7 +5785,7 @@ void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
     }
 
     // first we build the underlying array
-    ZigValue *array_val = create_const_vals(1);
+    ZigValue *array_val = g->pass1_arena->create<ZigValue>();
     array_val->special = ConstValSpecialStatic;
     array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), g->intern.for_zero_byte());
     array_val->data.x_array.special = ConstArraySpecialBuf;
@@ -5803,7 +5802,7 @@ void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
 }
 
 ZigValue *create_const_str_lit(CodeGen *g, Buf *str) {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_str_lit(g, const_val, str);
     return const_val;
 }
@@ -5814,8 +5813,8 @@ void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint)
     bigint_init_bigint(&const_val->data.x_bigint, bigint);
 }
 
-ZigValue *create_const_bigint(ZigType *type, const BigInt *bigint) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_bigint(const_val, type, bigint);
     return const_val;
 }
@@ -5828,8 +5827,8 @@ void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x
     const_val->data.x_bigint.is_negative = negative;
 }
 
-ZigValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_unsigned_negative(const_val, type, x, negative);
     return const_val;
 }
@@ -5839,7 +5838,7 @@ void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x) {
 }
 
 ZigValue *create_const_usize(CodeGen *g, uint64_t x) {
-    return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false);
+    return create_const_unsigned_negative(g, g->builtin_types.entry_usize, x, false);
 }
 
 void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) {
@@ -5848,8 +5847,8 @@ void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) {
     bigint_init_signed(&const_val->data.x_bigint, x);
 }
 
-ZigValue *create_const_signed(ZigType *type, int64_t x) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_signed(const_val, type, x);
     return const_val;
 }
@@ -5860,8 +5859,8 @@ void init_const_null(ZigValue *const_val, ZigType *type) {
     const_val->data.x_optional = nullptr;
 }
 
-ZigValue *create_const_null(ZigType *type) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_null(CodeGen *g, ZigType *type) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_null(const_val, type);
     return const_val;
 }
@@ -5893,8 +5892,8 @@ void init_const_float(ZigValue *const_val, ZigType *type, double value) {
     }
 }
 
-ZigValue *create_const_float(ZigType *type, double value) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_float(CodeGen *g, ZigType *type, double value) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_float(const_val, type, value);
     return const_val;
 }
@@ -5905,8 +5904,8 @@ void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag) {
     bigint_init_bigint(&const_val->data.x_enum_tag, tag);
 }
 
-ZigValue *create_const_enum(ZigType *type, const BigInt *tag) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_enum(const_val, type, tag);
     return const_val;
 }
@@ -5919,7 +5918,7 @@ void init_const_bool(CodeGen *g, ZigValue *const_val, bool value) {
 }
 
 ZigValue *create_const_bool(CodeGen *g, bool value) {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_bool(g, const_val, value);
     return const_val;
 }
@@ -5929,8 +5928,8 @@ void init_const_runtime(ZigValue *const_val, ZigType *type) {
     const_val->type = type;
 }
 
-ZigValue *create_const_runtime(ZigType *type) {
-    ZigValue *const_val = create_const_vals(1);
+ZigValue *create_const_runtime(CodeGen *g, ZigType *type) {
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_runtime(const_val, type);
     return const_val;
 }
@@ -5942,7 +5941,7 @@ void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value) {
 }
 
 ZigValue *create_const_type(CodeGen *g, ZigType *type_value) {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_type(g, const_val, type_value);
     return const_val;
 }
@@ -5957,7 +5956,7 @@ void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
 
     const_val->special = ConstValSpecialStatic;
     const_val->type = get_slice_type(g, ptr_type);
-    const_val->data.x_struct.fields = alloc_const_vals_ptrs(2);
+    const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 2);
 
     init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const,
             PtrLenUnknown);
@@ -5965,7 +5964,7 @@ void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
 }
 
 ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const) {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_slice(g, const_val, array_val, start, len, is_const);
     return const_val;
 }
@@ -5987,7 +5986,7 @@ void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
 ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const,
         PtrLen ptr_len)
 {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len);
     return const_val;
 }
@@ -6000,7 +5999,7 @@ void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val,
 }
 
 ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const) {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_ptr_ref(g, const_val, pointee_val, is_const);
     return const_val;
 }
@@ -6017,25 +6016,21 @@ void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *po
 ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
         size_t addr, bool is_const)
 {
-    ZigValue *const_val = create_const_vals(1);
+    ZigValue *const_val = g->pass1_arena->create<ZigValue>();
     init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const);
     return const_val;
 }
 
-ZigValue *create_const_vals(size_t count) {
-    return allocate<ZigValue>(count, "ZigValue");
+ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count) {
+    return realloc_const_vals_ptrs(g, nullptr, 0, count);
 }
 
-ZigValue **alloc_const_vals_ptrs(size_t count) {
-    return realloc_const_vals_ptrs(nullptr, 0, count);
-}
-
-ZigValue **realloc_const_vals_ptrs(ZigValue **ptr, size_t old_count, size_t new_count) {
+ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count) {
     assert(new_count >= old_count);
 
     size_t new_item_count = new_count - old_count;
-    ZigValue **result = reallocate(ptr, old_count, new_count, "ZigValue*");
-    ZigValue *vals = create_const_vals(new_item_count);
+    ZigValue **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
+    ZigValue *vals = g->pass1_arena->allocate<ZigValue>(new_item_count);
     for (size_t i = old_count; i < new_count; i += 1) {
         result[i] = &vals[i - old_count];
     }
@@ -6050,8 +6045,8 @@ TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_c
     assert(new_count >= old_count);
 
     size_t new_item_count = new_count - old_count;
-    TypeStructField **result = reallocate(ptr, old_count, new_count, "TypeStructField*");
-    TypeStructField *vals = allocate<TypeStructField>(new_item_count, "TypeStructField");
+    TypeStructField **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
+    TypeStructField *vals = heap::c_allocator.allocate<TypeStructField>(new_item_count);
     for (size_t i = old_count; i < new_count; i += 1) {
         result[i] = &vals[i - old_count];
     }
@@ -6062,7 +6057,7 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
     if (orig_fn_type->data.fn.fn_type_id.cc == CallingConventionAsync)
         return orig_fn_type;
 
-    ZigType *fn_type = allocate_nonzero<ZigType>(1);
+    ZigType *fn_type = heap::c_allocator.allocate_nonzero<ZigType>(1);
     *fn_type = *orig_fn_type;
     fn_type->data.fn.fn_type_id.cc = CallingConventionAsync;
     fn_type->llvm_type = nullptr;
@@ -6236,11 +6231,11 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
     ZigType *fn_type = get_async_fn_type(g, fn->type_entry);
 
     if (fn->analyzed_executable.need_err_code_spill) {
-        IrInstGenAlloca *alloca_gen = allocate<IrInstGenAlloca>(1);
+        IrInstGenAlloca *alloca_gen = heap::c_allocator.create<IrInstGenAlloca>();
         alloca_gen->base.id = IrInstGenIdAlloca;
         alloca_gen->base.base.source_node = fn->proto_node;
         alloca_gen->base.base.scope = fn->child_scope;
-        alloca_gen->base.value = allocate<ZigValue>(1, "ZigValue");
+        alloca_gen->base.value = g->pass1_arena->create<ZigValue>();
         alloca_gen->base.value->type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
         alloca_gen->base.base.ref_count = 1;
         alloca_gen->name_hint = "";
@@ -7375,7 +7370,7 @@ static void init_const_undefined(CodeGen *g, ZigValue *const_val) {
 
         const_val->special = ConstValSpecialStatic;
         size_t field_count = wanted_type->data.structure.src_field_count;
-        const_val->data.x_struct.fields = alloc_const_vals_ptrs(field_count);
+        const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
         for (size_t i = 0; i < field_count; i += 1) {
             ZigValue *field_val = const_val->data.x_struct.fields[i];
             field_val->type = resolve_struct_field_type(g, wanted_type->data.structure.fields[i]);
@@ -7418,7 +7413,7 @@ void expand_undef_array(CodeGen *g, ZigValue *const_val) {
             return;
         case ConstArraySpecialUndef: {
             const_val->data.x_array.special = ConstArraySpecialNone;
-            const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
+            const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(elem_count);
             for (size_t i = 0; i < elem_count; i += 1) {
                 ZigValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
                 element_val->type = elem_type;
@@ -7437,7 +7432,7 @@ void expand_undef_array(CodeGen *g, ZigValue *const_val) {
 
             const_val->data.x_array.special = ConstArraySpecialNone;
             assert(elem_count == buf_len(buf));
-            const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
+            const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(elem_count);
             for (size_t i = 0; i < elem_count; i += 1) {
                 ZigValue *this_char = &const_val->data.x_array.data.s_none.elements[i];
                 this_char->special = ConstValSpecialStatic;
@@ -7609,7 +7604,7 @@ const char *type_id_name(ZigTypeId id) {
 }
 
 LinkLib *create_link_lib(Buf *name) {
-    LinkLib *link_lib = allocate<LinkLib>(1);
+    LinkLib *link_lib = heap::c_allocator.create<LinkLib>();
     link_lib->name = name;
     return link_lib;
 }
@@ -8137,7 +8132,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
 
     size_t field_count = struct_type->data.structure.src_field_count;
     // Every field could potentially have a generated padding field after it.
-    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count * 2);
+    LLVMTypeRef *element_types = heap::c_allocator.allocate<LLVMTypeRef>(field_count * 2);
 
     bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
     size_t packed_bits_offset = 0;
@@ -8272,7 +8267,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
                 (unsigned)struct_type->data.structure.gen_field_count, packed);
     }
 
-    ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(debug_field_count);
+    ZigLLVMDIType **di_element_types = heap::c_allocator.allocate<ZigLLVMDIType*>(debug_field_count);
     size_t debug_field_index = 0;
     for (size_t i = 0; i < field_count; i += 1) {
         TypeStructField *field = struct_type->data.structure.fields[i];
@@ -8389,7 +8384,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
     uint32_t field_count = enum_type->data.enumeration.src_field_count;
 
     assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr);
-    ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
+    ZigLLVMDIEnumerator **di_enumerators = heap::c_allocator.allocate<ZigLLVMDIEnumerator*>(field_count);
 
     for (uint32_t i = 0; i < field_count; i += 1) {
         TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i];
@@ -8456,7 +8451,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
         if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
     }
 
-    ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
+    ZigLLVMDIType **union_inner_di_types = heap::c_allocator.allocate<ZigLLVMDIType*>(gen_field_count);
     uint32_t field_count = union_type->data.unionation.src_field_count;
     for (uint32_t i = 0; i < field_count; i += 1) {
         TypeUnionField *union_field = &union_type->data.unionation.fields[i];
@@ -8895,7 +8890,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
         param_di_types.append(get_llvm_di_type(g, gen_type));
     }
     if (is_async) {
-        fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(2);
+        fn_type->data.fn.gen_param_info = heap::c_allocator.allocate<FnGenParamInfo>(2);
 
         ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type);
         gen_param_types.append(get_llvm_type(g, frame_type));
@@ -8912,7 +8907,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
         fn_type->data.fn.gen_param_info[1].gen_index = 1;
         fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize;
     } else {
-        fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
+        fn_type->data.fn.gen_param_info = heap::c_allocator.allocate<FnGenParamInfo>(fn_type_id->param_count);
         for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
             FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
             ZigType *type_entry = src_param_info->type;
@@ -9369,7 +9364,7 @@ bool type_has_optional_repr(ZigType *ty) {
     }
 }
 
-void copy_const_val(ZigValue *dest, ZigValue *src) {
+void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
     uint32_t prev_align = dest->llvm_align;
     ConstParent prev_parent = dest->parent;
     memcpy(dest, src, sizeof(ZigValue));
@@ -9378,26 +9373,26 @@ void copy_const_val(ZigValue *dest, ZigValue *src) {
         return;
     dest->parent = prev_parent;
     if (dest->type->id == ZigTypeIdStruct) {
-        dest->data.x_struct.fields = alloc_const_vals_ptrs(dest->type->data.structure.src_field_count);
+        dest->data.x_struct.fields = alloc_const_vals_ptrs(g, dest->type->data.structure.src_field_count);
         for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
-            copy_const_val(dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
+            copy_const_val(g, dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
             dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct;
             dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest;
             dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
         }
     } else if (dest->type->id == ZigTypeIdArray) {
         if (dest->data.x_array.special == ConstArraySpecialNone) {
-            dest->data.x_array.data.s_none.elements = create_const_vals(dest->type->data.array.len);
+            dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(dest->type->data.array.len);
             for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) {
-                copy_const_val(&dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
+                copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
                 dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray;
                 dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest;
                 dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i;
             }
         }
     } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
-        dest->data.x_optional = create_const_vals(1);
-        copy_const_val(dest->data.x_optional, src->data.x_optional);
+        dest->data.x_optional = g->pass1_arena->create<ZigValue>();
+        copy_const_val(g, dest->data.x_optional, src->data.x_optional);
         dest->data.x_optional->parent.id = ConstParentIdOptionalPayload;
         dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest;
     }
src/analyze.hpp
@@ -128,22 +128,22 @@ void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str);
 ZigValue *create_const_str_lit(CodeGen *g, Buf *str);
 
 void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint);
-ZigValue *create_const_bigint(ZigType *type, const BigInt *bigint);
+ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint);
 
 void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative);
-ZigValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative);
+ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative);
 
 void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x);
-ZigValue *create_const_signed(ZigType *type, int64_t x);
+ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x);
 
 void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x);
 ZigValue *create_const_usize(CodeGen *g, uint64_t x);
 
 void init_const_float(ZigValue *const_val, ZigType *type, double value);
-ZigValue *create_const_float(ZigType *type, double value);
+ZigValue *create_const_float(CodeGen *g, ZigType *type, double value);
 
 void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag);
-ZigValue *create_const_enum(ZigType *type, const BigInt *tag);
+ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag);
 
 void init_const_bool(CodeGen *g, ZigValue *const_val, bool value);
 ZigValue *create_const_bool(CodeGen *g, bool value);
@@ -152,7 +152,7 @@ void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value);
 ZigValue *create_const_type(CodeGen *g, ZigType *type_value);
 
 void init_const_runtime(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_runtime(ZigType *type);
+ZigValue *create_const_runtime(CodeGen *g, ZigType *type);
 
 void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const);
 ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const);
@@ -172,11 +172,10 @@ void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
 ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const);
 
 void init_const_null(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_null(ZigType *type);
+ZigValue *create_const_null(CodeGen *g, ZigType *type);
 
-ZigValue *create_const_vals(size_t count);
-ZigValue **alloc_const_vals_ptrs(size_t count);
-ZigValue **realloc_const_vals_ptrs(ZigValue **ptr, size_t old_count, size_t new_count);
+ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
+ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);
 
 TypeStructField **alloc_type_struct_fields(size_t count);
 TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count);
@@ -275,7 +274,7 @@ Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_targe
         ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path);
 ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry);
 bool is_anon_container(ZigType *ty);
-void copy_const_val(ZigValue *dest, ZigValue *src);
+void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);
 bool type_has_optional_repr(ZigType *ty);
 bool is_opt_err_set(ZigType *ty);
 bool type_is_numeric(ZigType *ty);
src/bigint.cpp
@@ -93,7 +93,7 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
         if (dest->data.digit == 0) dest->digit_count = 0;
         return;
     }
-    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
     for (size_t i = 0; i < digits_to_copy; i += 1) {
         uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
         dest->data.digits[i] = digit;
@@ -174,7 +174,7 @@ void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count,
 
     dest->digit_count = digit_count;
     dest->is_negative = is_negative;
-    dest->data.digits = allocate_nonzero<uint64_t>(digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(digit_count);
     memcpy(dest->data.digits, digits, sizeof(uint64_t) * digit_count);
 
     bigint_normalize(dest);
@@ -191,13 +191,13 @@ void bigint_init_bigint(BigInt *dest, const BigInt *src) {
     }
     dest->is_negative = src->is_negative;
     dest->digit_count = src->digit_count;
-    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
     memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count);
 }
 
 void bigint_deinit(BigInt *bi) {
     if (bi->digit_count > 1)
-        deallocate<uint64_t>(bi->data.digits, bi->digit_count);
+        heap::c_allocator.deallocate(bi->data.digits, bi->digit_count);
 }
 
 void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) {
@@ -227,7 +227,7 @@ void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) {
     f128M_rem(&abs_val, &max_u64, &remainder);
 
     dest->digit_count = 2;
-    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
     dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false);
     dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false);
     bigint_normalize(dest);
@@ -345,7 +345,7 @@ void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_co
     if (dest->digit_count == 1) {
         digits = &dest->data.digit;
     } else {
-        digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
         dest->data.digits = digits;
     }
 
@@ -464,7 +464,7 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
         }
         size_t i = 1;
         uint64_t first_digit = dest->data.digit;
-        dest->data.digits = allocate_nonzero<uint64_t>(max(op1->digit_count, op2->digit_count) + 1);
+        dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(max(op1->digit_count, op2->digit_count) + 1);
         dest->data.digits[0] = first_digit;
 
         for (;;) {
@@ -532,7 +532,7 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
         return;
     }
     uint64_t first_digit = dest->data.digit;
-    dest->data.digits = allocate_nonzero<uint64_t>(bigger_op->digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(bigger_op->digit_count);
     dest->data.digits[0] = first_digit;
     size_t i = 1;
 
@@ -1032,7 +1032,7 @@ static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigIn
         if (lhsWords == 1) {
             Quotient->data.digit = Make_64(Q[1], Q[0]);
         } else {
-            Quotient->data.digits = allocate<uint64_t>(lhsWords);
+            Quotient->data.digits = heap::c_allocator.allocate<uint64_t>(lhsWords);
             for (size_t i = 0; i < lhsWords; i += 1) {
                 Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
             }
@@ -1046,7 +1046,7 @@ static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigIn
         if (rhsWords == 1) {
             Remainder->data.digit = Make_64(R[1], R[0]);
         } else {
-            Remainder->data.digits = allocate<uint64_t>(rhsWords);
+            Remainder->data.digits = heap::c_allocator.allocate<uint64_t>(rhsWords);
             for (size_t i = 0; i < rhsWords; i += 1) {
                 Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
             }
@@ -1218,7 +1218,7 @@ void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) {
             return;
         }
         dest->digit_count = max(op1->digit_count, op2->digit_count);
-        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
         for (size_t i = 0; i < dest->digit_count; i += 1) {
             uint64_t digit = 0;
             if (i < op1->digit_count) {
@@ -1262,7 +1262,7 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
         }
 
         dest->digit_count = max(op1->digit_count, op2->digit_count);
-        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
 
         size_t i = 0;
         for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
@@ -1308,7 +1308,7 @@ void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
             return;
         }
         dest->digit_count = max(op1->digit_count, op2->digit_count);
-        dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+        dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
         size_t i = 0;
         for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
             dest->data.digits[i] = op1_digits[i] ^ op2_digits[i];
@@ -1358,7 +1358,7 @@ void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) {
     uint64_t digit_shift_count = shift_amt / 64;
     uint64_t leftover_shift_count = shift_amt % 64;
 
-    dest->data.digits = allocate<uint64_t>(op1->digit_count + digit_shift_count + 1);
+    dest->data.digits = heap::c_allocator.allocate<uint64_t>(op1->digit_count + digit_shift_count + 1);
     dest->digit_count = digit_shift_count;
     uint64_t carry = 0;
     for (size_t i = 0; i < op1->digit_count; i += 1) {
@@ -1421,7 +1421,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
     if (dest->digit_count == 1) {
         digits = &dest->data.digit;
     } else {
-        digits = allocate<uint64_t>(dest->digit_count);
+        digits = heap::c_allocator.allocate<uint64_t>(dest->digit_count);
         dest->data.digits = digits;
     }
 
@@ -1492,7 +1492,7 @@ void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed
     }
     dest->digit_count = (bit_count + 63) / 64;
     assert(dest->digit_count >= op->digit_count);
-    dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
+    dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
     size_t i = 0;
     for (; i < op->digit_count; i += 1) {
         dest->data.digits[i] = ~op_digits[i];
src/buffer.hpp
@@ -50,7 +50,7 @@ static inline void buf_resize(Buf *buf, size_t new_len) {
 }
 
 static inline Buf *buf_alloc_fixed(size_t size) {
-    Buf *buf = allocate<Buf>(1);
+    Buf *buf = heap::c_allocator.create<Buf>();
     buf_resize(buf, size);
     return buf;
 }
@@ -65,7 +65,7 @@ static inline void buf_deinit(Buf *buf) {
 
 static inline void buf_destroy(Buf *buf) {
     buf_deinit(buf);
-    free(buf);
+    heap::c_allocator.destroy(buf);
 }
 
 static inline void buf_init_from_mem(Buf *buf, const char *ptr, size_t len) {
@@ -85,7 +85,7 @@ static inline void buf_init_from_buf(Buf *buf, Buf *other) {
 
 static inline Buf *buf_create_from_mem(const char *ptr, size_t len) {
     assert(len != SIZE_MAX);
-    Buf *buf = allocate<Buf>(1);
+    Buf *buf = heap::c_allocator.create<Buf>();
     buf_init_from_mem(buf, ptr, len);
     return buf;
 }
@@ -108,7 +108,7 @@ static inline Buf *buf_slice(Buf *in_buf, size_t start, size_t end) {
     assert(end != SIZE_MAX);
     assert(start < buf_len(in_buf));
     assert(end <= buf_len(in_buf));
-    Buf *out_buf = allocate<Buf>(1);
+    Buf *out_buf = heap::c_allocator.create<Buf>();
     out_buf->list.resize(end - start + 1);
     memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start);
     out_buf->list.at(buf_len(out_buf)) = 0;
@@ -211,5 +211,4 @@ static inline void buf_replace(Buf* buf, char from, char to) {
     }
 }
 
-
 #endif
src/codegen.cpp
@@ -21,6 +21,7 @@
 #include "userland.h"
 #include "dump_analysis.hpp"
 #include "softfloat.hpp"
+#include "mem_profile.hpp"
 
 #include <stdio.h>
 #include <errno.h>
@@ -57,7 +58,7 @@ static void init_darwin_native(CodeGen *g) {
 }
 
 static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) {
-    ZigPackage *entry = allocate<ZigPackage>(1);
+    ZigPackage *entry = heap::c_allocator.create<ZigPackage>();
     entry->package_table.init(4);
     buf_init_from_str(&entry->root_src_dir, root_src_dir);
     buf_init_from_str(&entry->root_src_path, root_src_path);
@@ -4327,7 +4328,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn
             }
             size_t field_count = arg_calc.field_index;
 
-            LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
+            LLVMTypeRef *field_types = heap::c_allocator.allocate_nonzero<LLVMTypeRef>(field_count);
             LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types);
             assert(LLVMCountStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc))) == arg_calc_start.field_index);
 
@@ -4680,8 +4681,8 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I
                                  instruction->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);
+    LLVMTypeRef *param_types = heap::c_allocator.allocate<LLVMTypeRef>(input_and_output_count);
+    LLVMValueRef *param_values = heap::c_allocator.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);
@@ -4923,7 +4924,7 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutableGen *execut
     // second vector. These start at -1 and go down, and are easiest to use
     // with the ~ operator. Here we convert between the two formats.
     IrInstGen *mask = instruction->mask;
-    LLVMValueRef *values = allocate<LLVMValueRef>(len_mask);
+    LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(len_mask);
     for (uint64_t i = 0; i < len_mask; i++) {
         if (mask->value->data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) {
             values[i] = LLVMGetUndef(LLVMInt32Type());
@@ -4935,7 +4936,7 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutableGen *execut
     }
 
     LLVMValueRef llvm_mask_value = LLVMConstVector(values, len_mask);
-    free(values);
+    heap::c_allocator.deallocate(values, len_mask);
 
     return LLVMBuildShuffleVector(g->builder,
         ir_llvm_value(g, instruction->a),
@@ -5003,8 +5004,8 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrIns
     }
 
     LLVMValueRef phi = LLVMBuildPhi(g->builder, phi_type, "");
-    LLVMValueRef *incoming_values = allocate<LLVMValueRef>(instruction->incoming_count);
-    LLVMBasicBlockRef *incoming_blocks = allocate<LLVMBasicBlockRef>(instruction->incoming_count);
+    LLVMValueRef *incoming_values = heap::c_allocator.allocate<LLVMValueRef>(instruction->incoming_count);
+    LLVMBasicBlockRef *incoming_blocks = heap::c_allocator.allocate<LLVMBasicBlockRef>(instruction->incoming_count);
     for (size_t i = 0; i < instruction->incoming_count; i += 1) {
         incoming_values[i] = ir_llvm_value(g, instruction->incoming_values[i]);
         incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block;
@@ -5977,12 +5978,12 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutableGen *executable, IrI
     LLVMValueRef shift_amt = LLVMConstInt(get_llvm_type(g, extended_type), 8, false);
     if (is_vector) {
         extended_type = get_vector_type(g, expr_type->data.vector.len, extended_type);
-        LLVMValueRef *values = allocate_nonzero<LLVMValueRef>(expr_type->data.vector.len);
+        LLVMValueRef *values = heap::c_allocator.allocate_nonzero<LLVMValueRef>(expr_type->data.vector.len);
         for (uint32_t i = 0; i < expr_type->data.vector.len; i += 1) {
             values[i] = shift_amt;
         }
         shift_amt = LLVMConstVector(values, expr_type->data.vector.len);
-        free(values);
+        heap::c_allocator.deallocate(values, expr_type->data.vector.len);
     }
     // aabbcc
     LLVMValueRef extended = LLVMBuildZExt(g->builder, op, get_llvm_type(g, extended_type), "");
@@ -7015,7 +7016,7 @@ check: switch (const_val->special) {
             }
         case ZigTypeIdStruct:
             {
-                LLVMValueRef *fields = allocate<LLVMValueRef>(type_entry->data.structure.gen_field_count);
+                LLVMValueRef *fields = heap::c_allocator.allocate<LLVMValueRef>(type_entry->data.structure.gen_field_count);
                 size_t src_field_count = type_entry->data.structure.src_field_count;
                 bool make_unnamed_struct = false;
                 assert(type_entry->data.structure.resolve_status == ResolveStatusLLVMFull);
@@ -7074,7 +7075,7 @@ check: switch (const_val->special) {
                             } else {
                                 const LLVMValueRef AMT = LLVMConstInt(LLVMTypeOf(val), 8, false);
 
-                                LLVMValueRef *values = allocate<LLVMValueRef>(size_in_bytes);
+                                LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(size_in_bytes);
                                 for (size_t i = 0; i < size_in_bytes; i++) {
                                     const size_t idx = is_big_endian ? size_in_bytes - 1 - i : i;
                                     values[idx] = LLVMConstTruncOrBitCast(val, LLVMInt8Type());
@@ -7138,7 +7139,7 @@ check: switch (const_val->special) {
                     case ConstArraySpecialNone: {
                         uint64_t extra_len_from_sentinel = (type_entry->data.array.sentinel != nullptr) ? 1 : 0;
                         uint64_t full_len = len + extra_len_from_sentinel;
-                        LLVMValueRef *values = allocate<LLVMValueRef>(full_len);
+                        LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(full_len);
                         LLVMTypeRef element_type_ref = get_llvm_type(g, type_entry->data.array.child_type);
                         bool make_unnamed_struct = false;
                         for (uint64_t i = 0; i < len; i += 1) {
@@ -7170,7 +7171,7 @@ check: switch (const_val->special) {
                 case ConstArraySpecialUndef:
                     return LLVMGetUndef(get_llvm_type(g, type_entry));
                 case ConstArraySpecialNone: {
-                    LLVMValueRef *values = allocate<LLVMValueRef>(len);
+                    LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(len);
                     for (uint64_t i = 0; i < len; i += 1) {
                         ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
                         values[i] = gen_const_val(g, elem_value, "");
@@ -7180,7 +7181,7 @@ check: switch (const_val->special) {
                 case ConstArraySpecialBuf: {
                     Buf *buf = const_val->data.x_array.data.s_buf;
                     assert(buf_len(buf) == len);
-                    LLVMValueRef *values = allocate<LLVMValueRef>(len);
+                    LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(len);
                     for (uint64_t i = 0; i < len; i += 1) {
                         values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false);
                     }
@@ -7382,7 +7383,7 @@ static void generate_error_name_table(CodeGen *g) {
             PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false);
     ZigType *str_type = get_slice_type(g, u8_ptr_type);
 
-    LLVMValueRef *values = allocate<LLVMValueRef>(g->errors_by_index.length);
+    LLVMValueRef *values = heap::c_allocator.allocate<LLVMValueRef>(g->errors_by_index.length);
     values[0] = LLVMGetUndef(get_llvm_type(g, str_type));
     for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
         ErrorTableEntry *err_entry = g->errors_by_index.at(i);
@@ -7911,6 +7912,9 @@ static void do_code_gen(CodeGen *g) {
 }
 
 static void zig_llvm_emit_output(CodeGen *g) {
+    g->pass1_arena->destruct(&heap::c_allocator);
+    g->pass1_arena = nullptr;
+
     bool is_small = g->build_mode == BuildModeSmallRelease;
 
     Buf *output_path = &g->o_file_output_path;
@@ -8207,7 +8211,7 @@ static void define_intern_values(CodeGen *g) {
 }
 
 static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name, size_t count) {
-    BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
+    BuiltinFnEntry *builtin_fn = heap::c_allocator.create<BuiltinFnEntry>();
     buf_init_from_str(&builtin_fn->name, name);
     builtin_fn->id = id;
     builtin_fn->param_count = count;
@@ -8925,16 +8929,16 @@ static void init(CodeGen *g) {
     define_builtin_types(g);
     define_intern_values(g);
 
-    IrInstGen *sentinel_instructions = allocate<IrInstGen>(2);
+    IrInstGen *sentinel_instructions = heap::c_allocator.allocate<IrInstGen>(2);
     g->invalid_inst_gen = &sentinel_instructions[0];
-    g->invalid_inst_gen->value = allocate<ZigValue>(1, "ZigValue");
+    g->invalid_inst_gen->value = g->pass1_arena->create<ZigValue>();
     g->invalid_inst_gen->value->type = g->builtin_types.entry_invalid;
 
     g->unreach_instruction = &sentinel_instructions[1];
-    g->unreach_instruction->value = allocate<ZigValue>(1, "ZigValue");
+    g->unreach_instruction->value = g->pass1_arena->create<ZigValue>();
     g->unreach_instruction->value->type = g->builtin_types.entry_unreachable;
 
-    g->invalid_inst_src = allocate<IrInstSrc>(1);
+    g->invalid_inst_src = heap::c_allocator.create<IrInstSrc>();
 
     define_builtin_fns(g);
     Error err;
@@ -9016,7 +9020,7 @@ static void detect_libc(CodeGen *g) {
                 buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os));
 
         g->libc_include_dir_len = 4;
-        g->libc_include_dir_list = allocate<Buf*>(g->libc_include_dir_len);
+        g->libc_include_dir_list = heap::c_allocator.allocate<Buf*>(g->libc_include_dir_len);
         g->libc_include_dir_list[0] = arch_include_dir;
         g->libc_include_dir_list[1] = generic_include_dir;
         g->libc_include_dir_list[2] = arch_os_include_dir;
@@ -9025,7 +9029,7 @@ static void detect_libc(CodeGen *g) {
     }
 
     if (g->zig_target->is_native) {
-        g->libc = allocate<ZigLibCInstallation>(1);
+        g->libc = heap::c_allocator.create<ZigLibCInstallation>();
 
         // search for native_libc.txt in following dirs:
         //   - LOCAL_CACHE_DIR
@@ -9105,7 +9109,7 @@ static void detect_libc(CodeGen *g) {
         size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0;
         size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs;
         g->libc_include_dir_len = 0;
-        g->libc_include_dir_list = allocate<Buf*>(dir_count);
+        g->libc_include_dir_list = heap::c_allocator.allocate<Buf*>(dir_count);
 
         g->libc_include_dir_list[g->libc_include_dir_len] = &g->libc->include_dir;
         g->libc_include_dir_len += 1;
@@ -9472,10 +9476,10 @@ static void update_test_functions_builtin_decl(CodeGen *g) {
     if ((err = type_resolve(g, struct_type, ResolveStatusSizeKnown)))
         zig_unreachable();
 
-    ZigValue *test_fn_array = create_const_vals(1);
+    ZigValue *test_fn_array = g->pass1_arena->create<ZigValue>();
     test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, nullptr);
     test_fn_array->special = ConstValSpecialStatic;
-    test_fn_array->data.x_array.data.s_none.elements = create_const_vals(g->test_fns.length);
+    test_fn_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(g->test_fns.length);
 
     for (size_t i = 0; i < g->test_fns.length; i += 1) {
         ZigFn *test_fn_entry = g->test_fns.at(i);
@@ -9486,7 +9490,7 @@ static void update_test_functions_builtin_decl(CodeGen *g) {
         this_val->parent.id = ConstParentIdArray;
         this_val->parent.data.p_array.array_val = test_fn_array;
         this_val->parent.data.p_array.elem_index = i;
-        this_val->data.x_struct.fields = alloc_const_vals_ptrs(3);
+        this_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 3);
 
         ZigValue *name_field = this_val->data.x_struct.fields[0];
         ZigValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name)->data.x_ptr.data.ref.pointee;
@@ -9505,7 +9509,7 @@ static void update_test_functions_builtin_decl(CodeGen *g) {
         frame_size_field->data.x_optional = nullptr;
 
         if (fn_is_async(test_fn_entry)) {
-            frame_size_field->data.x_optional = create_const_vals(1);
+            frame_size_field->data.x_optional = g->pass1_arena->create<ZigValue>();
             frame_size_field->data.x_optional->special = ConstValSpecialStatic;
             frame_size_field->data.x_optional->type = g->builtin_types.entry_usize;
             bigint_init_unsigned(&frame_size_field->data.x_optional->data.x_bigint,
@@ -9640,7 +9644,7 @@ static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix) {
 
 Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose) {
     Error err;
-    CacheHash *cache_hash = allocate<CacheHash>(1);
+    CacheHash *cache_hash = heap::c_allocator.create<CacheHash>();
     Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(g->cache_dir));
     cache_init(cache_hash, manifest_dir);
 
@@ -10794,7 +10798,8 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
     OutType out_type, BuildMode build_mode, Buf *override_lib_dir,
     ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node)
 {
-    CodeGen *g = allocate<CodeGen>(1);
+    CodeGen *g = heap::c_allocator.create<CodeGen>();
+    g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
     g->main_progress_node = progress_node;
 
     codegen_add_time_event(g, "Initialize");
@@ -10937,35 +10942,35 @@ void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) {
 
 ZigValue *CodeGen::Intern::for_undefined() {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_intern_count.x_undefined += 1;
+    mem::intern_counters.x_undefined += 1;
 #endif
     return &this->x_undefined;
 }
 
 ZigValue *CodeGen::Intern::for_void() {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_intern_count.x_void += 1;
+    mem::intern_counters.x_void += 1;
 #endif
     return &this->x_void;
 }
 
 ZigValue *CodeGen::Intern::for_null() {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_intern_count.x_null += 1;
+    mem::intern_counters.x_null += 1;
 #endif
     return &this->x_null;
 }
 
 ZigValue *CodeGen::Intern::for_unreachable() {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_intern_count.x_unreachable += 1;
+    mem::intern_counters.x_unreachable += 1;
 #endif
     return &this->x_unreachable;
 }
 
 ZigValue *CodeGen::Intern::for_zero_byte() {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_intern_count.zero_byte += 1;
+    mem::intern_counters.zero_byte += 1;
 #endif
     return &this->zero_byte;
 }
src/errmsg.cpp
@@ -99,7 +99,7 @@ void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) {
 ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset,
         const char *source, Buf *msg)
 {
-    ErrorMsg *err_msg = allocate<ErrorMsg>(1);
+    ErrorMsg *err_msg = heap::c_allocator.create<ErrorMsg>();
     err_msg->path = path;
     err_msg->line_start = line;
     err_msg->column_start = column;
@@ -138,7 +138,7 @@ ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size
 ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column,
         Buf *source, ZigList<size_t> *line_offsets, Buf *msg)
 {
-    ErrorMsg *err_msg = allocate<ErrorMsg>(1);
+    ErrorMsg *err_msg = heap::c_allocator.create<ErrorMsg>();
     err_msg->path = path;
     err_msg->line_start = line;
     err_msg->column_start = column;
src/glibc.cpp
@@ -21,7 +21,7 @@ static const ZigGLibCLib glibc_libs[] = {
 Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) {
     Error err;
 
-    ZigGLibCAbi *glibc_abi = allocate<ZigGLibCAbi>(1);
+    ZigGLibCAbi *glibc_abi = heap::c_allocator.create<ZigGLibCAbi>();
     glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir));
     glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir));
     glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir));
@@ -100,10 +100,10 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo
                 Optional<Slice<uint8_t>> opt_line = SplitIterator_next_separate(&it);
                 if (!opt_line.is_some) break;
 
-                ver_list_base = allocate<ZigGLibCVerList>(glibc_abi->all_functions.length);
+                ver_list_base = heap::c_allocator.allocate<ZigGLibCVerList>(glibc_abi->all_functions.length);
                 SplitIterator line_it = memSplit(opt_line.value, str(" "));
                 for (;;) {
-                    ZigTarget *target = allocate<ZigTarget>(1);
+                    ZigTarget *target = heap::c_allocator.create<ZigTarget>();
                     Optional<Slice<uint8_t>> opt_target = SplitIterator_next(&line_it);
                     if (!opt_target.is_some) break;
 
@@ -174,7 +174,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
     Error err;
 
     Buf *cache_dir = get_global_cache_dir();
-    CacheHash *cache_hash = allocate<CacheHash>(1);
+    CacheHash *cache_hash = heap::c_allocator.create<CacheHash>();
     Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir));
     cache_init(cache_hash, manifest_dir);
 
src/hash_map.hpp
@@ -19,7 +19,7 @@ public:
         init_capacity(capacity);
     }
     void deinit(void) {
-        free(_entries);
+        heap::c_allocator.deallocate(_entries, _capacity);
     }
 
     struct Entry {
@@ -57,7 +57,7 @@ public:
                 if (old_entry->used)
                     internal_put(old_entry->key, old_entry->value);
             }
-            free(old_entries);
+            heap::c_allocator.deallocate(old_entries, old_capacity);
         }
     }
 
@@ -164,7 +164,7 @@ private:
 
     void init_capacity(int capacity) {
         _capacity = capacity;
-        _entries = allocate<Entry>(_capacity);
+        _entries = heap::c_allocator.allocate<Entry>(_capacity);
         _size = 0;
         _max_distance_from_start_index = 0;
         for (int i = 0; i < _capacity; i += 1) {
src/heap.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include <new>
+#include <string.h>
+
+#include "config.h"
+#include "heap.hpp"
+#include "mem_profile.hpp"
+
+namespace heap {
+
+extern mem::Allocator &bootstrap_allocator;
+
+//
+// BootstrapAllocator implementation is identical to CAllocator minus
+// profile profile functionality. Splitting off to a base interface doesn't
+// seem worthwhile.
+//
+
+void BootstrapAllocator::init(const char *name) {}
+void BootstrapAllocator::deinit() {}
+
+void *BootstrapAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+    return mem::os::calloc(count, info.size);
+}
+
+void *BootstrapAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+    return mem::os::malloc(count * info.size);
+}
+
+void *BootstrapAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+    auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+    if (new_count > old_count)
+        memset(reinterpret_cast<uint8_t *>(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size);
+    return new_ptr;
+}
+
+void *BootstrapAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+    return mem::os::realloc(old_ptr, new_count * info.size);
+}
+
+void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+    mem::os::free(ptr);
+}
+
+void CAllocator::init(const char *name) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile = bootstrap_allocator.create<mem::Profile>();
+    this->profile->init(name, "CAllocator");
+#endif
+}
+
+void CAllocator::deinit() {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    assert(this->profile);
+    this->profile->deinit();
+    bootstrap_allocator.destroy(this->profile);
+    this->profile = nullptr;
+#endif
+}
+
+CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) {
+    auto p = new(allocator->create<CAllocator>()) CAllocator();
+    p->init(name);
+    return p;
+}
+
+void CAllocator::destruct(mem::Allocator *allocator) {
+    this->deinit();
+    allocator->destroy(this);
+}
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void CAllocator::print_report(FILE *file) {
+    this->profile->print_report(file);
+}
+#endif
+
+void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_alloc(info, count);
+#endif
+    return mem::os::calloc(count, info.size);
+}
+
+void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_alloc(info, count);
+#endif
+    return mem::os::malloc(count * info.size);
+}
+
+void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+    auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+    if (new_count > old_count)
+        memset(reinterpret_cast<uint8_t *>(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size);
+    return new_ptr;
+}
+
+void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_dealloc(info, old_count);
+    this->profile->record_alloc(info, new_count);
+#endif
+    return mem::os::realloc(old_ptr, new_count * info.size);
+}
+
+void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_dealloc(info, count);
+#endif
+    mem::os::free(ptr);
+}
+
+struct ArenaAllocator::Impl {
+    Allocator *backing;
+
+    // regular allocations bump through a segment of static size
+    struct Segment {
+        static constexpr size_t size = 65536;
+        static constexpr size_t object_threshold = 4096;
+
+        uint8_t data[size];
+    };
+
+    // active segment
+    Segment *segment;
+    size_t segment_offset;
+
+    // keep track of segments
+    struct SegmentTrack {
+        static constexpr size_t size = (4096 - sizeof(SegmentTrack *)) / sizeof(Segment *);
+
+        // null if first
+        SegmentTrack *prev;
+        Segment *segments[size];
+    };
+    static_assert(sizeof(SegmentTrack) <= 4096, "unwanted struct padding");
+
+    // active segment track
+    SegmentTrack *segment_track;
+    size_t segment_track_remain;
+
+    // individual allocations punted to backing allocator
+    struct Object {
+        uint8_t *ptr;
+        size_t len;
+    };
+
+    // keep track of objects
+    struct ObjectTrack {
+        static constexpr size_t size = (4096 - sizeof(ObjectTrack *)) / sizeof(Object);
+
+        // null if first
+        ObjectTrack *prev;
+        Object objects[size];
+    };
+    static_assert(sizeof(ObjectTrack) <= 4096, "unwanted struct padding");
+
+    // active object track
+    ObjectTrack *object_track;
+    size_t object_track_remain;
+
+    ATTRIBUTE_RETURNS_NOALIAS inline void *allocate(const mem::TypeInfo& info, size_t count);
+    inline void *reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count);
+
+    inline void new_segment();
+    inline void track_segment();
+    inline void track_object(Object object);
+};
+
+void *ArenaAllocator::Impl::allocate(const mem::TypeInfo& info, size_t count) {
+#ifndef NDEBUG
+    // make behavior when size == 0 portable
+    if (info.size == 0 || count == 0)
+        return nullptr;
+#endif
+    const size_t nbytes = info.size * count;
+    this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1);
+    if (nbytes >= Segment::object_threshold) {
+        auto ptr = this->backing->allocate<uint8_t>(nbytes);
+        this->track_object({ptr, nbytes});
+        return ptr;
+    }
+    if (this->segment_offset + nbytes > Segment::size)
+        this->new_segment();
+    auto ptr = &this->segment->data[this->segment_offset];
+    this->segment_offset += nbytes;
+    return ptr;
+}
+
+void *ArenaAllocator::Impl::reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifndef NDEBUG
+    // make behavior when size == 0 portable
+    if (info.size == 0 && old_ptr == nullptr)
+        return nullptr;
+#endif
+    const size_t new_nbytes = info.size * new_count;
+    if (new_nbytes <= info.size * old_count)
+        return old_ptr;
+    const size_t old_nbytes = info.size * old_count;
+    this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1);
+    if (new_nbytes >= Segment::object_threshold) {
+        auto new_ptr = this->backing->allocate<uint8_t>(new_nbytes);
+        this->track_object({new_ptr, new_nbytes});
+        memcpy(new_ptr, old_ptr, old_nbytes);
+        return new_ptr;
+    }
+    if (this->segment_offset + new_nbytes > Segment::size)
+        this->new_segment();
+    auto new_ptr = &this->segment->data[this->segment_offset];
+    this->segment_offset += new_nbytes;
+    memcpy(new_ptr, old_ptr, old_nbytes);
+    return new_ptr;
+}
+
+void ArenaAllocator::Impl::new_segment() {
+    this->segment = this->backing->create<Segment>();
+    this->segment_offset = 0;
+    this->track_segment();
+}
+
+void ArenaAllocator::Impl::track_segment() {
+    assert(this->segment != nullptr);
+    if (this->segment_track_remain < 1) {
+        auto prev = this->segment_track;
+        this->segment_track = this->backing->create<SegmentTrack>();
+        this->segment_track->prev = prev;
+        this->segment_track_remain = SegmentTrack::size;
+    }
+    this->segment_track_remain -= 1;
+    this->segment_track->segments[this->segment_track_remain] = this->segment;
+}
+
+void ArenaAllocator::Impl::track_object(Object object) {
+    if (this->object_track_remain < 1) {
+        auto prev = this->object_track;
+        this->object_track = this->backing->create<ObjectTrack>();
+        this->object_track->prev = prev;
+        this->object_track_remain = ObjectTrack::size;
+    }
+    this->object_track_remain -= 1;
+    this->object_track->objects[this->object_track_remain] = object;
+}
+
+void ArenaAllocator::init(Allocator *backing, const char *name) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile = bootstrap_allocator.create<mem::Profile>();
+    this->profile->init(name, "ArenaAllocator");
+#endif
+    this->impl = bootstrap_allocator.create<Impl>();
+    {
+        auto &r = *this->impl;
+        r.backing = backing;
+        r.segment_offset = Impl::Segment::size;
+    }
+}
+
+void ArenaAllocator::deinit() {
+    auto &backing = *this->impl->backing;
+
+    // segments
+    if (this->impl->segment_track) {
+        // active track is not full and bounded by track_remain
+        auto prev = this->impl->segment_track->prev;
+        {
+            auto t = this->impl->segment_track;
+            for (size_t i = this->impl->segment_track_remain; i < Impl::SegmentTrack::size; ++i)
+                backing.destroy(t->segments[i]);
+            backing.destroy(t);
+        }
+
+        // previous tracks are full
+        for (auto t = prev; t != nullptr;) {
+            for (size_t i = 0; i < Impl::SegmentTrack::size; ++i)
+                backing.destroy(t->segments[i]);
+            prev = t->prev;
+            backing.destroy(t);
+            t = prev;
+        }
+    }
+
+    // objects
+    if (this->impl->object_track) {
+        // active track is not full and bounded by track_remain
+        auto prev = this->impl->object_track->prev;
+        {
+            auto t = this->impl->object_track;
+            for (size_t i = this->impl->object_track_remain; i < Impl::ObjectTrack::size; ++i) {
+                auto &obj = t->objects[i];
+                backing.deallocate(obj.ptr, obj.len);
+            }
+            backing.destroy(t);
+        }
+
+        // previous tracks are full
+        for (auto t = prev; t != nullptr;) {
+            for (size_t i = 0; i < Impl::ObjectTrack::size; ++i) {
+                auto &obj = t->objects[i];
+                backing.deallocate(obj.ptr, obj.len);
+            }
+            prev = t->prev;
+            backing.destroy(t);
+            t = prev;
+        }
+    }
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    assert(this->profile);
+    this->profile->deinit();
+    bootstrap_allocator.destroy(this->profile);
+    this->profile = nullptr;
+#endif
+}
+
+ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) {
+    auto p = new(allocator->create<ArenaAllocator>()) ArenaAllocator;
+    p->init(backing, name);
+    return p;
+}
+
+void ArenaAllocator::destruct(mem::Allocator *allocator) {
+    this->deinit();
+    allocator->destroy(this);
+}
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void ArenaAllocator::print_report(FILE *file) {
+    this->profile->print_report(file);
+}
+#endif
+
+void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_alloc(info, count);
+#endif
+    return this->impl->allocate(info, count);
+}
+
+void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_alloc(info, count);
+#endif
+    return this->impl->allocate(info, count);
+}
+
+void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+    return this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+}
+
+void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_dealloc(info, old_count);
+    this->profile->record_alloc(info, new_count);
+#endif
+    return this->impl->reallocate(info, old_ptr, old_count, new_count);
+}
+
+void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    this->profile->record_dealloc(info, count);
+#endif
+    // noop
+}
+
+BootstrapAllocator bootstrap_allocator_state;
+mem::Allocator &bootstrap_allocator = bootstrap_allocator_state;
+
+CAllocator c_allocator_state;
+mem::Allocator &c_allocator = c_allocator_state;
+
+} // namespace heap
src/heap.hpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_HEAP_HPP
+#define ZIG_HEAP_HPP
+
+#include "config.h"
+#include "util_base.hpp"
+#include "mem.hpp"
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+namespace mem {
+    struct Profile;
+}
+#endif
+
+namespace heap {
+
+struct BootstrapAllocator final : mem::Allocator {
+    void init(const char *name);
+    void deinit();
+    void destruct(Allocator *allocator) {}
+
+private:
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final;
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final;
+    void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final;
+};
+
+struct CAllocator final : mem::Allocator {
+    void init(const char *name);
+    void deinit();
+
+    static CAllocator *construct(mem::Allocator *allocator, const char *name);
+    void destruct(mem::Allocator *allocator) final;
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    void print_report(FILE *file = nullptr);
+#endif
+
+private:
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final;
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final;
+    void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final;
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    mem::Profile *profile;
+#endif
+};
+
+//
+// arena allocator
+//
+// - allocations are backed by the underlying allocator's memory
+// - allocations are N:1 relationship to underlying allocations
+// - dellocations are noops
+// - deinit() releases all underlying memory
+//
+struct ArenaAllocator final : mem::Allocator {
+    void init(Allocator *backing, const char *name);
+    void deinit();
+
+    static ArenaAllocator *construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name);
+    void destruct(mem::Allocator *allocator) final;
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    void print_report(FILE *file = nullptr);
+#endif
+
+private:
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final;
+    ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final;
+    void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
+    void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final;
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    mem::Profile *profile;
+#endif
+
+    struct Impl;
+    Impl *impl;
+};
+
+extern BootstrapAllocator bootstrap_allocator_state;
+extern mem::Allocator &bootstrap_allocator;
+
+extern CAllocator c_allocator_state;
+extern mem::Allocator &c_allocator;
+
+} // namespace heap
+
+#endif
src/ir.cpp
@@ -269,477 +269,467 @@ static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst,
 static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr);
 
 static void destroy_instruction_src(IrInstSrc *inst) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    const char *name = ir_inst_src_type_str(inst->id);
-#else
-    const char *name = nullptr;
-#endif
     switch (inst->id) {
         case IrInstSrcIdInvalid:
             zig_unreachable();
         case IrInstSrcIdReturn:
-            return destroy(reinterpret_cast<IrInstSrcReturn *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturn *>(inst));
         case IrInstSrcIdConst:
-            return destroy(reinterpret_cast<IrInstSrcConst *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcConst *>(inst));
         case IrInstSrcIdBinOp:
-            return destroy(reinterpret_cast<IrInstSrcBinOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBinOp *>(inst));
         case IrInstSrcIdMergeErrSets:
-            return destroy(reinterpret_cast<IrInstSrcMergeErrSets *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMergeErrSets *>(inst));
         case IrInstSrcIdDeclVar:
-            return destroy(reinterpret_cast<IrInstSrcDeclVar *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclVar *>(inst));
         case IrInstSrcIdCall:
-            return destroy(reinterpret_cast<IrInstSrcCall *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
         case IrInstSrcIdCallExtra:
-            return destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
         case IrInstSrcIdUnOp:
-            return destroy(reinterpret_cast<IrInstSrcUnOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
         case IrInstSrcIdCondBr:
-            return destroy(reinterpret_cast<IrInstSrcCondBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCondBr *>(inst));
         case IrInstSrcIdBr:
-            return destroy(reinterpret_cast<IrInstSrcBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBr *>(inst));
         case IrInstSrcIdPhi:
-            return destroy(reinterpret_cast<IrInstSrcPhi *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPhi *>(inst));
         case IrInstSrcIdContainerInitList:
-            return destroy(reinterpret_cast<IrInstSrcContainerInitList *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitList *>(inst));
         case IrInstSrcIdContainerInitFields:
-            return destroy(reinterpret_cast<IrInstSrcContainerInitFields *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitFields *>(inst));
         case IrInstSrcIdUnreachable:
-            return destroy(reinterpret_cast<IrInstSrcUnreachable *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnreachable *>(inst));
         case IrInstSrcIdElemPtr:
-            return destroy(reinterpret_cast<IrInstSrcElemPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcElemPtr *>(inst));
         case IrInstSrcIdVarPtr:
-            return destroy(reinterpret_cast<IrInstSrcVarPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVarPtr *>(inst));
         case IrInstSrcIdLoadPtr:
-            return destroy(reinterpret_cast<IrInstSrcLoadPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcLoadPtr *>(inst));
         case IrInstSrcIdStorePtr:
-            return destroy(reinterpret_cast<IrInstSrcStorePtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcStorePtr *>(inst));
         case IrInstSrcIdTypeOf:
-            return destroy(reinterpret_cast<IrInstSrcTypeOf *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeOf *>(inst));
         case IrInstSrcIdFieldPtr:
-            return destroy(reinterpret_cast<IrInstSrcFieldPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldPtr *>(inst));
         case IrInstSrcIdSetCold:
-            return destroy(reinterpret_cast<IrInstSrcSetCold *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetCold *>(inst));
         case IrInstSrcIdSetRuntimeSafety:
-            return destroy(reinterpret_cast<IrInstSrcSetRuntimeSafety *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetRuntimeSafety *>(inst));
         case IrInstSrcIdSetFloatMode:
-            return destroy(reinterpret_cast<IrInstSrcSetFloatMode *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetFloatMode *>(inst));
         case IrInstSrcIdArrayType:
-            return destroy(reinterpret_cast<IrInstSrcArrayType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArrayType *>(inst));
         case IrInstSrcIdSliceType:
-            return destroy(reinterpret_cast<IrInstSrcSliceType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSliceType *>(inst));
         case IrInstSrcIdAnyFrameType:
-            return destroy(reinterpret_cast<IrInstSrcAnyFrameType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAnyFrameType *>(inst));
         case IrInstSrcIdAsm:
-            return destroy(reinterpret_cast<IrInstSrcAsm *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsm *>(inst));
         case IrInstSrcIdSizeOf:
-            return destroy(reinterpret_cast<IrInstSrcSizeOf *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSizeOf *>(inst));
         case IrInstSrcIdTestNonNull:
-            return destroy(reinterpret_cast<IrInstSrcTestNonNull *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestNonNull *>(inst));
         case IrInstSrcIdOptionalUnwrapPtr:
-            return destroy(reinterpret_cast<IrInstSrcOptionalUnwrapPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOptionalUnwrapPtr *>(inst));
         case IrInstSrcIdPopCount:
-            return destroy(reinterpret_cast<IrInstSrcPopCount *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPopCount *>(inst));
         case IrInstSrcIdClz:
-            return destroy(reinterpret_cast<IrInstSrcClz *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcClz *>(inst));
         case IrInstSrcIdCtz:
-            return destroy(reinterpret_cast<IrInstSrcCtz *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCtz *>(inst));
         case IrInstSrcIdBswap:
-            return destroy(reinterpret_cast<IrInstSrcBswap *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBswap *>(inst));
         case IrInstSrcIdBitReverse:
-            return destroy(reinterpret_cast<IrInstSrcBitReverse *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitReverse *>(inst));
         case IrInstSrcIdSwitchBr:
-            return destroy(reinterpret_cast<IrInstSrcSwitchBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchBr *>(inst));
         case IrInstSrcIdSwitchVar:
-            return destroy(reinterpret_cast<IrInstSrcSwitchVar *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchVar *>(inst));
         case IrInstSrcIdSwitchElseVar:
-            return destroy(reinterpret_cast<IrInstSrcSwitchElseVar *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchElseVar *>(inst));
         case IrInstSrcIdSwitchTarget:
-            return destroy(reinterpret_cast<IrInstSrcSwitchTarget *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchTarget *>(inst));
         case IrInstSrcIdImport:
-            return destroy(reinterpret_cast<IrInstSrcImport *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImport *>(inst));
         case IrInstSrcIdRef:
-            return destroy(reinterpret_cast<IrInstSrcRef *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcRef *>(inst));
         case IrInstSrcIdCompileErr:
-            return destroy(reinterpret_cast<IrInstSrcCompileErr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileErr *>(inst));
         case IrInstSrcIdCompileLog:
-            return destroy(reinterpret_cast<IrInstSrcCompileLog *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileLog *>(inst));
         case IrInstSrcIdErrName:
-            return destroy(reinterpret_cast<IrInstSrcErrName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrName *>(inst));
         case IrInstSrcIdCImport:
-            return destroy(reinterpret_cast<IrInstSrcCImport *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCImport *>(inst));
         case IrInstSrcIdCInclude:
-            return destroy(reinterpret_cast<IrInstSrcCInclude *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCInclude *>(inst));
         case IrInstSrcIdCDefine:
-            return destroy(reinterpret_cast<IrInstSrcCDefine *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCDefine *>(inst));
         case IrInstSrcIdCUndef:
-            return destroy(reinterpret_cast<IrInstSrcCUndef *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCUndef *>(inst));
         case IrInstSrcIdEmbedFile:
-            return destroy(reinterpret_cast<IrInstSrcEmbedFile *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEmbedFile *>(inst));
         case IrInstSrcIdCmpxchg:
-            return destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst));
         case IrInstSrcIdFence:
-            return destroy(reinterpret_cast<IrInstSrcFence *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst));
         case IrInstSrcIdTruncate:
-            return destroy(reinterpret_cast<IrInstSrcTruncate *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst));
         case IrInstSrcIdIntCast:
-            return destroy(reinterpret_cast<IrInstSrcIntCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntCast *>(inst));
         case IrInstSrcIdFloatCast:
-            return destroy(reinterpret_cast<IrInstSrcFloatCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatCast *>(inst));
         case IrInstSrcIdErrSetCast:
-            return destroy(reinterpret_cast<IrInstSrcErrSetCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrSetCast *>(inst));
         case IrInstSrcIdFromBytes:
-            return destroy(reinterpret_cast<IrInstSrcFromBytes *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFromBytes *>(inst));
         case IrInstSrcIdToBytes:
-            return destroy(reinterpret_cast<IrInstSrcToBytes *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcToBytes *>(inst));
         case IrInstSrcIdIntToFloat:
-            return destroy(reinterpret_cast<IrInstSrcIntToFloat *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToFloat *>(inst));
         case IrInstSrcIdFloatToInt:
-            return destroy(reinterpret_cast<IrInstSrcFloatToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatToInt *>(inst));
         case IrInstSrcIdBoolToInt:
-            return destroy(reinterpret_cast<IrInstSrcBoolToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolToInt *>(inst));
         case IrInstSrcIdIntType:
-            return destroy(reinterpret_cast<IrInstSrcIntType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntType *>(inst));
         case IrInstSrcIdVectorType:
-            return destroy(reinterpret_cast<IrInstSrcVectorType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVectorType *>(inst));
         case IrInstSrcIdShuffleVector:
-            return destroy(reinterpret_cast<IrInstSrcShuffleVector *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcShuffleVector *>(inst));
         case IrInstSrcIdSplat:
-            return destroy(reinterpret_cast<IrInstSrcSplat *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSplat *>(inst));
         case IrInstSrcIdBoolNot:
-            return destroy(reinterpret_cast<IrInstSrcBoolNot *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolNot *>(inst));
         case IrInstSrcIdMemset:
-            return destroy(reinterpret_cast<IrInstSrcMemset *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemset *>(inst));
         case IrInstSrcIdMemcpy:
-            return destroy(reinterpret_cast<IrInstSrcMemcpy *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemcpy *>(inst));
         case IrInstSrcIdSlice:
-            return destroy(reinterpret_cast<IrInstSrcSlice *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSlice *>(inst));
         case IrInstSrcIdMemberCount:
-            return destroy(reinterpret_cast<IrInstSrcMemberCount *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemberCount *>(inst));
         case IrInstSrcIdMemberType:
-            return destroy(reinterpret_cast<IrInstSrcMemberType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemberType *>(inst));
         case IrInstSrcIdMemberName:
-            return destroy(reinterpret_cast<IrInstSrcMemberName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemberName *>(inst));
         case IrInstSrcIdBreakpoint:
-            return destroy(reinterpret_cast<IrInstSrcBreakpoint *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBreakpoint *>(inst));
         case IrInstSrcIdReturnAddress:
-            return destroy(reinterpret_cast<IrInstSrcReturnAddress *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturnAddress *>(inst));
         case IrInstSrcIdFrameAddress:
-            return destroy(reinterpret_cast<IrInstSrcFrameAddress *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameAddress *>(inst));
         case IrInstSrcIdFrameHandle:
-            return destroy(reinterpret_cast<IrInstSrcFrameHandle *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameHandle *>(inst));
         case IrInstSrcIdFrameType:
-            return destroy(reinterpret_cast<IrInstSrcFrameType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameType *>(inst));
         case IrInstSrcIdFrameSize:
-            return destroy(reinterpret_cast<IrInstSrcFrameSize *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameSize *>(inst));
         case IrInstSrcIdAlignOf:
-            return destroy(reinterpret_cast<IrInstSrcAlignOf *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignOf *>(inst));
         case IrInstSrcIdOverflowOp:
-            return destroy(reinterpret_cast<IrInstSrcOverflowOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOverflowOp *>(inst));
         case IrInstSrcIdTestErr:
-            return destroy(reinterpret_cast<IrInstSrcTestErr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestErr *>(inst));
         case IrInstSrcIdUnwrapErrCode:
-            return destroy(reinterpret_cast<IrInstSrcUnwrapErrCode *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrCode *>(inst));
         case IrInstSrcIdUnwrapErrPayload:
-            return destroy(reinterpret_cast<IrInstSrcUnwrapErrPayload *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrPayload *>(inst));
         case IrInstSrcIdFnProto:
-            return destroy(reinterpret_cast<IrInstSrcFnProto *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFnProto *>(inst));
         case IrInstSrcIdTestComptime:
-            return destroy(reinterpret_cast<IrInstSrcTestComptime *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestComptime *>(inst));
         case IrInstSrcIdPtrCast:
-            return destroy(reinterpret_cast<IrInstSrcPtrCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrCast *>(inst));
         case IrInstSrcIdBitCast:
-            return destroy(reinterpret_cast<IrInstSrcBitCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitCast *>(inst));
         case IrInstSrcIdPtrToInt:
-            return destroy(reinterpret_cast<IrInstSrcPtrToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrToInt *>(inst));
         case IrInstSrcIdIntToPtr:
-            return destroy(reinterpret_cast<IrInstSrcIntToPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToPtr *>(inst));
         case IrInstSrcIdIntToEnum:
-            return destroy(reinterpret_cast<IrInstSrcIntToEnum *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToEnum *>(inst));
         case IrInstSrcIdIntToErr:
-            return destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
         case IrInstSrcIdErrToInt:
-            return destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
         case IrInstSrcIdCheckSwitchProngs:
-            return destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
         case IrInstSrcIdCheckStatementIsVoid:
-            return destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
         case IrInstSrcIdTypeName:
-            return destroy(reinterpret_cast<IrInstSrcTypeName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeName *>(inst));
         case IrInstSrcIdTagName:
-            return destroy(reinterpret_cast<IrInstSrcTagName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
         case IrInstSrcIdPtrType:
-            return destroy(reinterpret_cast<IrInstSrcPtrType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
         case IrInstSrcIdDeclRef:
-            return destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
         case IrInstSrcIdPanic:
-            return destroy(reinterpret_cast<IrInstSrcPanic *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPanic *>(inst));
         case IrInstSrcIdFieldParentPtr:
-            return destroy(reinterpret_cast<IrInstSrcFieldParentPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldParentPtr *>(inst));
         case IrInstSrcIdByteOffsetOf:
-            return destroy(reinterpret_cast<IrInstSrcByteOffsetOf *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcByteOffsetOf *>(inst));
         case IrInstSrcIdBitOffsetOf:
-            return destroy(reinterpret_cast<IrInstSrcBitOffsetOf *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitOffsetOf *>(inst));
         case IrInstSrcIdTypeInfo:
-            return destroy(reinterpret_cast<IrInstSrcTypeInfo *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeInfo *>(inst));
         case IrInstSrcIdType:
-            return destroy(reinterpret_cast<IrInstSrcType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcType *>(inst));
         case IrInstSrcIdHasField:
-            return destroy(reinterpret_cast<IrInstSrcHasField *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasField *>(inst));
         case IrInstSrcIdTypeId:
-            return destroy(reinterpret_cast<IrInstSrcTypeId *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeId *>(inst));
         case IrInstSrcIdSetEvalBranchQuota:
-            return destroy(reinterpret_cast<IrInstSrcSetEvalBranchQuota *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetEvalBranchQuota *>(inst));
         case IrInstSrcIdAlignCast:
-            return destroy(reinterpret_cast<IrInstSrcAlignCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignCast *>(inst));
         case IrInstSrcIdImplicitCast:
-            return destroy(reinterpret_cast<IrInstSrcImplicitCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImplicitCast *>(inst));
         case IrInstSrcIdResolveResult:
-            return destroy(reinterpret_cast<IrInstSrcResolveResult *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResolveResult *>(inst));
         case IrInstSrcIdResetResult:
-            return destroy(reinterpret_cast<IrInstSrcResetResult *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
         case IrInstSrcIdOpaqueType:
-            return destroy(reinterpret_cast<IrInstSrcOpaqueType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOpaqueType *>(inst));
         case IrInstSrcIdSetAlignStack:
-            return destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
         case IrInstSrcIdArgType:
-            return destroy(reinterpret_cast<IrInstSrcArgType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
         case IrInstSrcIdTagType:
-            return destroy(reinterpret_cast<IrInstSrcTagType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagType *>(inst));
         case IrInstSrcIdExport:
-            return destroy(reinterpret_cast<IrInstSrcExport *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
         case IrInstSrcIdErrorReturnTrace:
-            return destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
         case IrInstSrcIdErrorUnion:
-            return destroy(reinterpret_cast<IrInstSrcErrorUnion *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorUnion *>(inst));
         case IrInstSrcIdAtomicRmw:
-            return destroy(reinterpret_cast<IrInstSrcAtomicRmw *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicRmw *>(inst));
         case IrInstSrcIdSaveErrRetAddr:
-            return destroy(reinterpret_cast<IrInstSrcSaveErrRetAddr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSaveErrRetAddr *>(inst));
         case IrInstSrcIdAddImplicitReturnType:
-            return destroy(reinterpret_cast<IrInstSrcAddImplicitReturnType *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAddImplicitReturnType *>(inst));
         case IrInstSrcIdFloatOp:
-            return destroy(reinterpret_cast<IrInstSrcFloatOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatOp *>(inst));
         case IrInstSrcIdMulAdd:
-            return destroy(reinterpret_cast<IrInstSrcMulAdd *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMulAdd *>(inst));
         case IrInstSrcIdAtomicLoad:
-            return destroy(reinterpret_cast<IrInstSrcAtomicLoad *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicLoad *>(inst));
         case IrInstSrcIdAtomicStore:
-            return destroy(reinterpret_cast<IrInstSrcAtomicStore *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicStore *>(inst));
         case IrInstSrcIdEnumToInt:
-            return destroy(reinterpret_cast<IrInstSrcEnumToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEnumToInt *>(inst));
         case IrInstSrcIdCheckRuntimeScope:
-            return destroy(reinterpret_cast<IrInstSrcCheckRuntimeScope *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckRuntimeScope *>(inst));
         case IrInstSrcIdHasDecl:
-            return destroy(reinterpret_cast<IrInstSrcHasDecl *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasDecl *>(inst));
         case IrInstSrcIdUndeclaredIdent:
-            return destroy(reinterpret_cast<IrInstSrcUndeclaredIdent *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUndeclaredIdent *>(inst));
         case IrInstSrcIdAlloca:
-            return destroy(reinterpret_cast<IrInstSrcAlloca *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlloca *>(inst));
         case IrInstSrcIdEndExpr:
-            return destroy(reinterpret_cast<IrInstSrcEndExpr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEndExpr *>(inst));
         case IrInstSrcIdUnionInitNamedField:
-            return destroy(reinterpret_cast<IrInstSrcUnionInitNamedField *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnionInitNamedField *>(inst));
         case IrInstSrcIdSuspendBegin:
-            return destroy(reinterpret_cast<IrInstSrcSuspendBegin *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendBegin *>(inst));
         case IrInstSrcIdSuspendFinish:
-            return destroy(reinterpret_cast<IrInstSrcSuspendFinish *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendFinish *>(inst));
         case IrInstSrcIdResume:
-            return destroy(reinterpret_cast<IrInstSrcResume *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResume *>(inst));
         case IrInstSrcIdAwait:
-            return destroy(reinterpret_cast<IrInstSrcAwait *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAwait *>(inst));
         case IrInstSrcIdSpillBegin:
-            return destroy(reinterpret_cast<IrInstSrcSpillBegin *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillBegin *>(inst));
         case IrInstSrcIdSpillEnd:
-            return destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst));
         case IrInstSrcIdCallArgs:
-            return destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst));
     }
     zig_unreachable();
 }
 
 void destroy_instruction_gen(IrInstGen *inst) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    const char *name = ir_inst_gen_type_str(inst->id);
-#else
-    const char *name = nullptr;
-#endif
     switch (inst->id) {
         case IrInstGenIdInvalid:
             zig_unreachable();
         case IrInstGenIdReturn:
-            return destroy(reinterpret_cast<IrInstGenReturn *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReturn *>(inst));
         case IrInstGenIdConst:
-            return destroy(reinterpret_cast<IrInstGenConst *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenConst *>(inst));
         case IrInstGenIdBinOp:
-            return destroy(reinterpret_cast<IrInstGenBinOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBinOp *>(inst));
         case IrInstGenIdCast:
-            return destroy(reinterpret_cast<IrInstGenCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCast *>(inst));
         case IrInstGenIdCall:
-            return destroy(reinterpret_cast<IrInstGenCall *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCall *>(inst));
         case IrInstGenIdCondBr:
-            return destroy(reinterpret_cast<IrInstGenCondBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCondBr *>(inst));
         case IrInstGenIdBr:
-            return destroy(reinterpret_cast<IrInstGenBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBr *>(inst));
         case IrInstGenIdPhi:
-            return destroy(reinterpret_cast<IrInstGenPhi *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPhi *>(inst));
         case IrInstGenIdUnreachable:
-            return destroy(reinterpret_cast<IrInstGenUnreachable *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenUnreachable *>(inst));
         case IrInstGenIdElemPtr:
-            return destroy(reinterpret_cast<IrInstGenElemPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenElemPtr *>(inst));
         case IrInstGenIdVarPtr:
-            return destroy(reinterpret_cast<IrInstGenVarPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenVarPtr *>(inst));
         case IrInstGenIdReturnPtr:
-            return destroy(reinterpret_cast<IrInstGenReturnPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReturnPtr *>(inst));
         case IrInstGenIdLoadPtr:
-            return destroy(reinterpret_cast<IrInstGenLoadPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenLoadPtr *>(inst));
         case IrInstGenIdStorePtr:
-            return destroy(reinterpret_cast<IrInstGenStorePtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenStorePtr *>(inst));
         case IrInstGenIdVectorStoreElem:
-            return destroy(reinterpret_cast<IrInstGenVectorStoreElem *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenVectorStoreElem *>(inst));
         case IrInstGenIdStructFieldPtr:
-            return destroy(reinterpret_cast<IrInstGenStructFieldPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenStructFieldPtr *>(inst));
         case IrInstGenIdUnionFieldPtr:
-            return destroy(reinterpret_cast<IrInstGenUnionFieldPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenUnionFieldPtr *>(inst));
         case IrInstGenIdAsm:
-            return destroy(reinterpret_cast<IrInstGenAsm *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAsm *>(inst));
         case IrInstGenIdTestNonNull:
-            return destroy(reinterpret_cast<IrInstGenTestNonNull *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTestNonNull *>(inst));
         case IrInstGenIdOptionalUnwrapPtr:
-            return destroy(reinterpret_cast<IrInstGenOptionalUnwrapPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenOptionalUnwrapPtr *>(inst));
         case IrInstGenIdPopCount:
-            return destroy(reinterpret_cast<IrInstGenPopCount *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPopCount *>(inst));
         case IrInstGenIdClz:
-            return destroy(reinterpret_cast<IrInstGenClz *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenClz *>(inst));
         case IrInstGenIdCtz:
-            return destroy(reinterpret_cast<IrInstGenCtz *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCtz *>(inst));
         case IrInstGenIdBswap:
-            return destroy(reinterpret_cast<IrInstGenBswap *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBswap *>(inst));
         case IrInstGenIdBitReverse:
-            return destroy(reinterpret_cast<IrInstGenBitReverse *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBitReverse *>(inst));
         case IrInstGenIdSwitchBr:
-            return destroy(reinterpret_cast<IrInstGenSwitchBr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSwitchBr *>(inst));
         case IrInstGenIdUnionTag:
-            return destroy(reinterpret_cast<IrInstGenUnionTag *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenUnionTag *>(inst));
         case IrInstGenIdRef:
-            return destroy(reinterpret_cast<IrInstGenRef *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenRef *>(inst));
         case IrInstGenIdErrName:
-            return destroy(reinterpret_cast<IrInstGenErrName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenErrName *>(inst));
         case IrInstGenIdCmpxchg:
-            return destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst));
         case IrInstGenIdFence:
-            return destroy(reinterpret_cast<IrInstGenFence *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst));
         case IrInstGenIdTruncate:
-            return destroy(reinterpret_cast<IrInstGenTruncate *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst));
         case IrInstGenIdShuffleVector:
-            return destroy(reinterpret_cast<IrInstGenShuffleVector *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenShuffleVector *>(inst));
         case IrInstGenIdSplat:
-            return destroy(reinterpret_cast<IrInstGenSplat *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSplat *>(inst));
         case IrInstGenIdBoolNot:
-            return destroy(reinterpret_cast<IrInstGenBoolNot *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBoolNot *>(inst));
         case IrInstGenIdMemset:
-            return destroy(reinterpret_cast<IrInstGenMemset *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenMemset *>(inst));
         case IrInstGenIdMemcpy:
-            return destroy(reinterpret_cast<IrInstGenMemcpy *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenMemcpy *>(inst));
         case IrInstGenIdSlice:
-            return destroy(reinterpret_cast<IrInstGenSlice *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSlice *>(inst));
         case IrInstGenIdBreakpoint:
-            return destroy(reinterpret_cast<IrInstGenBreakpoint *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBreakpoint *>(inst));
         case IrInstGenIdReturnAddress:
-            return destroy(reinterpret_cast<IrInstGenReturnAddress *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReturnAddress *>(inst));
         case IrInstGenIdFrameAddress:
-            return destroy(reinterpret_cast<IrInstGenFrameAddress *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFrameAddress *>(inst));
         case IrInstGenIdFrameHandle:
-            return destroy(reinterpret_cast<IrInstGenFrameHandle *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFrameHandle *>(inst));
         case IrInstGenIdFrameSize:
-            return destroy(reinterpret_cast<IrInstGenFrameSize *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFrameSize *>(inst));
         case IrInstGenIdOverflowOp:
-            return destroy(reinterpret_cast<IrInstGenOverflowOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenOverflowOp *>(inst));
         case IrInstGenIdTestErr:
-            return destroy(reinterpret_cast<IrInstGenTestErr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTestErr *>(inst));
         case IrInstGenIdUnwrapErrCode:
-            return destroy(reinterpret_cast<IrInstGenUnwrapErrCode *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenUnwrapErrCode *>(inst));
         case IrInstGenIdUnwrapErrPayload:
-            return destroy(reinterpret_cast<IrInstGenUnwrapErrPayload *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenUnwrapErrPayload *>(inst));
         case IrInstGenIdOptionalWrap:
-            return destroy(reinterpret_cast<IrInstGenOptionalWrap *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenOptionalWrap *>(inst));
         case IrInstGenIdErrWrapCode:
-            return destroy(reinterpret_cast<IrInstGenErrWrapCode *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenErrWrapCode *>(inst));
         case IrInstGenIdErrWrapPayload:
-            return destroy(reinterpret_cast<IrInstGenErrWrapPayload *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenErrWrapPayload *>(inst));
         case IrInstGenIdPtrCast:
-            return destroy(reinterpret_cast<IrInstGenPtrCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPtrCast *>(inst));
         case IrInstGenIdBitCast:
-            return destroy(reinterpret_cast<IrInstGenBitCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBitCast *>(inst));
         case IrInstGenIdWidenOrShorten:
-            return destroy(reinterpret_cast<IrInstGenWidenOrShorten *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWidenOrShorten *>(inst));
         case IrInstGenIdPtrToInt:
-            return destroy(reinterpret_cast<IrInstGenPtrToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPtrToInt *>(inst));
         case IrInstGenIdIntToPtr:
-            return destroy(reinterpret_cast<IrInstGenIntToPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenIntToPtr *>(inst));
         case IrInstGenIdIntToEnum:
-            return destroy(reinterpret_cast<IrInstGenIntToEnum *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenIntToEnum *>(inst));
         case IrInstGenIdIntToErr:
-            return destroy(reinterpret_cast<IrInstGenIntToErr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenIntToErr *>(inst));
         case IrInstGenIdErrToInt:
-            return destroy(reinterpret_cast<IrInstGenErrToInt *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenErrToInt *>(inst));
         case IrInstGenIdTagName:
-            return destroy(reinterpret_cast<IrInstGenTagName *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTagName *>(inst));
         case IrInstGenIdPanic:
-            return destroy(reinterpret_cast<IrInstGenPanic *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPanic *>(inst));
         case IrInstGenIdFieldParentPtr:
-            return destroy(reinterpret_cast<IrInstGenFieldParentPtr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFieldParentPtr *>(inst));
         case IrInstGenIdAlignCast:
-            return destroy(reinterpret_cast<IrInstGenAlignCast *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAlignCast *>(inst));
         case IrInstGenIdErrorReturnTrace:
-            return destroy(reinterpret_cast<IrInstGenErrorReturnTrace *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenErrorReturnTrace *>(inst));
         case IrInstGenIdAtomicRmw:
-            return destroy(reinterpret_cast<IrInstGenAtomicRmw *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAtomicRmw *>(inst));
         case IrInstGenIdSaveErrRetAddr:
-            return destroy(reinterpret_cast<IrInstGenSaveErrRetAddr *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSaveErrRetAddr *>(inst));
         case IrInstGenIdFloatOp:
-            return destroy(reinterpret_cast<IrInstGenFloatOp *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFloatOp *>(inst));
         case IrInstGenIdMulAdd:
-            return destroy(reinterpret_cast<IrInstGenMulAdd *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenMulAdd *>(inst));
         case IrInstGenIdAtomicLoad:
-            return destroy(reinterpret_cast<IrInstGenAtomicLoad *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAtomicLoad *>(inst));
         case IrInstGenIdAtomicStore:
-            return destroy(reinterpret_cast<IrInstGenAtomicStore *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAtomicStore *>(inst));
         case IrInstGenIdDeclVar:
-            return destroy(reinterpret_cast<IrInstGenDeclVar *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenDeclVar *>(inst));
         case IrInstGenIdArrayToVector:
-            return destroy(reinterpret_cast<IrInstGenArrayToVector *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenArrayToVector *>(inst));
         case IrInstGenIdVectorToArray:
-            return destroy(reinterpret_cast<IrInstGenVectorToArray *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenVectorToArray *>(inst));
         case IrInstGenIdPtrOfArrayToSlice:
-            return destroy(reinterpret_cast<IrInstGenPtrOfArrayToSlice *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenPtrOfArrayToSlice *>(inst));
         case IrInstGenIdAssertZero:
-            return destroy(reinterpret_cast<IrInstGenAssertZero *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAssertZero *>(inst));
         case IrInstGenIdAssertNonNull:
-            return destroy(reinterpret_cast<IrInstGenAssertNonNull *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAssertNonNull *>(inst));
         case IrInstGenIdResizeSlice:
-            return destroy(reinterpret_cast<IrInstGenResizeSlice *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenResizeSlice *>(inst));
         case IrInstGenIdAlloca:
-            return destroy(reinterpret_cast<IrInstGenAlloca *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAlloca *>(inst));
         case IrInstGenIdSuspendBegin:
-            return destroy(reinterpret_cast<IrInstGenSuspendBegin *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSuspendBegin *>(inst));
         case IrInstGenIdSuspendFinish:
-            return destroy(reinterpret_cast<IrInstGenSuspendFinish *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSuspendFinish *>(inst));
         case IrInstGenIdResume:
-            return destroy(reinterpret_cast<IrInstGenResume *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenResume *>(inst));
         case IrInstGenIdAwait:
-            return destroy(reinterpret_cast<IrInstGenAwait *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenAwait *>(inst));
         case IrInstGenIdSpillBegin:
-            return destroy(reinterpret_cast<IrInstGenSpillBegin *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSpillBegin *>(inst));
         case IrInstGenIdSpillEnd:
-            return destroy(reinterpret_cast<IrInstGenSpillEnd *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenSpillEnd *>(inst));
         case IrInstGenIdVectorExtractElem:
-            return destroy(reinterpret_cast<IrInstGenVectorExtractElem *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenVectorExtractElem *>(inst));
         case IrInstGenIdBinaryNot:
-            return destroy(reinterpret_cast<IrInstGenBinaryNot *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenBinaryNot *>(inst));
         case IrInstGenIdNegation:
-            return destroy(reinterpret_cast<IrInstGenNegation *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegation *>(inst));
         case IrInstGenIdNegationWrapping:
-            return destroy(reinterpret_cast<IrInstGenNegationWrapping *>(inst), name);
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegationWrapping *>(inst));
     }
     zig_unreachable();
 }
@@ -760,15 +750,14 @@ static void ira_deref(IrAnalyze *ira) {
             IrInstSrc *pass1_inst = pass1_bb->instruction_list.items[inst_i];
             destroy_instruction_src(pass1_inst);
         }
-        destroy(pass1_bb, "IrBasicBlockSrc");
+        heap::c_allocator.destroy(pass1_bb);
     }
     ira->old_irb.exec->basic_block_list.deinit();
     ira->old_irb.exec->tld_list.deinit();
-    // cannot destroy here because of var->owner_exec
-    //destroy(ira->old_irb.exec, "IrExecutableSrc");
+    heap::c_allocator.destroy(ira->old_irb.exec);
     ira->src_implicit_return_type_list.deinit();
     ira->resume_stack.deinit();
-    destroy(ira, "IrAnalyze");
+    heap::c_allocator.destroy(ira);
 }
 
 static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_val) {
@@ -1017,8 +1006,8 @@ static void ir_ref_var(ZigVar *var) {
 static void create_result_ptr(CodeGen *codegen, ZigType *expected_type,
         ZigValue **out_result, ZigValue **out_result_ptr)
 {
-    ZigValue *result = create_const_vals(1);
-    ZigValue *result_ptr = create_const_vals(1);
+    ZigValue *result = codegen->pass1_arena->create<ZigValue>();
+    ZigValue *result_ptr = codegen->pass1_arena->create<ZigValue>();
     result->special = ConstValSpecialUndef;
     result->type = expected_type;
     result_ptr->special = ConstValSpecialStatic;
@@ -1050,14 +1039,11 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
     assert(result->special != ConstValSpecialRuntime);
     ZigType *res_type = result->data.x_type;
 
-    destroy(result_ptr, "ZigValue");
-    destroy(result, "ZigValue");
-
     return res_type;
 }
 
 static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) {
-    IrBasicBlockSrc *result = allocate<IrBasicBlockSrc>(1, "IrBasicBlockSrc");
+    IrBasicBlockSrc *result = heap::c_allocator.create<IrBasicBlockSrc>();
     result->scope = scope;
     result->name_hint = name_hint;
     result->debug_id = exec_next_debug_id(irb->exec);
@@ -1066,7 +1052,7 @@ static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, c
 }
 
 static IrBasicBlockGen *ir_create_basic_block_gen(IrAnalyze *ira, Scope *scope, const char *name_hint) {
-    IrBasicBlockGen *result = allocate<IrBasicBlockGen>(1, "IrBasicBlockGen");
+    IrBasicBlockGen *result = heap::c_allocator.create<IrBasicBlockGen>();
     result->scope = scope;
     result->name_hint = name_hint;
     result->debug_id = exec_next_debug_id_gen(ira->new_irb.exec);
@@ -1983,12 +1969,7 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) {
 
 template<typename T>
 static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    const char *name = nullptr;
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    T *dummy = nullptr;
-    name = ir_inst_src_type_str(ir_inst_id(dummy));
-#endif
-    T *special_instruction = allocate<T>(1, name);
+    T *special_instruction = heap::c_allocator.create<T>();
     special_instruction->base.id = ir_inst_id(special_instruction);
     special_instruction->base.base.scope = scope;
     special_instruction->base.base.source_node = source_node;
@@ -1999,29 +1980,19 @@ static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source
 
 template<typename T>
 static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    const char *name = nullptr;
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    T *dummy = nullptr;
-    name = ir_inst_gen_type_str(ir_inst_id(dummy));
-#endif
-    T *special_instruction = allocate<T>(1, name);
+    T *special_instruction = heap::c_allocator.create<T>();
     special_instruction->base.id = ir_inst_id(special_instruction);
     special_instruction->base.base.scope = scope;
     special_instruction->base.base.source_node = source_node;
     special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec);
     special_instruction->base.owner_bb = irb->current_basic_block;
-    special_instruction->base.value = allocate<ZigValue>(1, "ZigValue");
+    special_instruction->base.value = irb->codegen->pass1_arena->create<ZigValue>();
     return special_instruction;
 }
 
 template<typename T>
 static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    const char *name = nullptr;
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    T *dummy = nullptr;
-    name = ir_inst_gen_type_str(ir_inst_id(dummy));
-#endif
-    T *special_instruction = allocate<T>(1, name);
+    T *special_instruction = heap::c_allocator.create<T>();
     special_instruction->base.id = ir_inst_id(special_instruction);
     special_instruction->base.base.scope = scope;
     special_instruction->base.base.source_node = source_node;
@@ -2063,11 +2034,11 @@ static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_no
 IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
         ZigType *var_type, const char *name_hint)
 {
-    IrInstGenAlloca *alloca_gen = allocate<IrInstGenAlloca>(1);
+    IrInstGenAlloca *alloca_gen = heap::c_allocator.create<IrInstGenAlloca>();
     alloca_gen->base.id = IrInstGenIdAlloca;
     alloca_gen->base.base.source_node = source_node;
     alloca_gen->base.base.scope = scope;
-    alloca_gen->base.value = allocate<ZigValue>(1, "ZigValue");
+    alloca_gen->base.value = g->pass1_arena->create<ZigValue>();
     alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false);
     alloca_gen->base.base.ref_count = 1;
     alloca_gen->name_hint = name_hint;
@@ -2157,7 +2128,7 @@ static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstN
 
 static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
     const_instruction->value->special = ConstValSpecialStatic;
     bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
@@ -2166,7 +2137,7 @@ static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *
 
 static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigInt *bigint) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
     const_instruction->value->special = ConstValSpecialStatic;
     bigint_init_bigint(&const_instruction->value->data.x_bigint, bigint);
@@ -2175,7 +2146,7 @@ static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode
 
 static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float;
     const_instruction->value->special = ConstValSpecialStatic;
     bigfloat_init_bigfloat(&const_instruction->value->data.x_bigfloat, bigfloat);
@@ -2191,7 +2162,7 @@ static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *
 
 static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_usize;
     const_instruction->value->special = ConstValSpecialStatic;
     bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
@@ -2202,7 +2173,7 @@ static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode
         ZigType *type_entry)
 {
     IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_type;
     const_instruction->value->special = ConstValSpecialStatic;
     const_instruction->value->data.x_type = type_entry;
@@ -2219,7 +2190,7 @@ static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *
 
 static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_type;
     const_instruction->value->special = ConstValSpecialStatic;
     const_instruction->value->data.x_type = import;
@@ -2228,7 +2199,7 @@ static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode
 
 static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_bool;
     const_instruction->value->special = ConstValSpecialStatic;
     const_instruction->value->data.x_bool = value;
@@ -2237,7 +2208,7 @@ static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *
 
 static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
     IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal;
     const_instruction->value->special = ConstValSpecialStatic;
     const_instruction->value->data.x_enum_literal = name;
@@ -2246,7 +2217,7 @@ static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, A
 
 static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
     IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = create_const_vals(1);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
     init_const_str_lit(irb->codegen, const_instruction->value, str);
 
     return &const_instruction->base;
@@ -5244,7 +5215,7 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node,
     switch (node->data.return_expr.kind) {
         case ReturnKindUnconditional:
             {
-                ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
+                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
                 result_loc_ret->base.id = ResultLocIdReturn;
                 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
 
@@ -5332,7 +5303,7 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node,
                 ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
                 IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val,
                         SpillIdRetErrCode);
-                ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
+                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
                 result_loc_ret->base.id = ResultLocIdReturn;
                 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
                 ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
@@ -5360,12 +5331,12 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
         Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime,
         bool skip_name_check)
 {
-    ZigVar *variable_entry = allocate<ZigVar>(1, "ZigVar");
+    ZigVar *variable_entry = heap::c_allocator.create<ZigVar>();
     variable_entry->parent_scope = parent_scope;
     variable_entry->shadowable = is_shadowable;
     variable_entry->is_comptime = is_comptime;
     variable_entry->src_arg_index = SIZE_MAX;
-    variable_entry->const_value = create_const_vals(1);
+    variable_entry->const_value = codegen->pass1_arena->create<ZigValue>();
 
     if (is_comptime != nullptr) {
         is_comptime->base.ref_count += 1;
@@ -5425,15 +5396,12 @@ static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf
     ZigVar *var = create_local_var(irb->codegen, node, scope,
             (is_underscored ? nullptr : name), src_is_const, gen_is_const,
             (is_underscored ? true : is_shadowable), is_comptime, false);
-    if (is_comptime != nullptr || gen_is_const) {
-        var->owner_exec = irb->exec;
-    }
     assert(var->child_scope);
     return var;
 }
 
 static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
-    ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer");
+    ResultLocPeer *result = heap::c_allocator.create<ResultLocPeer>();
     result->base.id = ResultLocIdPeer;
     result->base.source_instruction = peer_parent->base.source_instruction;
     result->parent = peer_parent;
@@ -5472,7 +5440,7 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
         scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
                 ir_should_inline(irb->exec, parent_scope));
 
-        scope_block->peer_parent = allocate<ResultLocPeerParent>(1, "ResultLocPeerParent");
+        scope_block->peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
         scope_block->peer_parent->base.id = ResultLocIdPeerParent;
         scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
         scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
@@ -5562,7 +5530,7 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
     // only generate unconditional defers
 
     ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr));
-    ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
+    ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
     result_loc_ret->base.id = ResultLocIdReturn;
     ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
     ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
@@ -5604,7 +5572,7 @@ static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node)
     if (lvalue == irb->codegen->invalid_inst_src)
         return irb->codegen->invalid_inst_src;
 
-    ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction");
+    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
     result_loc_inst->base.id = ResultLocIdInstruction;
     result_loc_inst->base.source_instruction = lvalue;
     ir_ref_instruction(lvalue, irb->current_basic_block);
@@ -5676,10 +5644,10 @@ static IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node)
 
     ir_set_cursor_at_end_and_append_block(irb, true_block);
 
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2, "IrInstSrc *");
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = val1;
     incoming_values[1] = val2;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = post_val1_block;
     incoming_blocks[1] = post_val2_block;
 
@@ -5718,10 +5686,10 @@ static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node
 
     ir_set_cursor_at_end_and_append_block(irb, false_block);
 
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = val1;
     incoming_values[1] = val2;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = post_val1_block;
     incoming_blocks[1] = post_val2_block;
 
@@ -5731,7 +5699,7 @@ static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node
 static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
         IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
 {
-    ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
+    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
     peer_parent->base.id = ResultLocIdPeerParent;
     peer_parent->base.source_instruction = cond_br_inst;
     peer_parent->base.allow_write_through_const = parent->allow_write_through_const;
@@ -5809,10 +5777,10 @@ static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode
     ir_build_br(irb, parent_scope, node, end_block, is_comptime);
 
     ir_set_cursor_at_end_and_append_block(irb, end_block);
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = null_result;
     incoming_values[1] = unwrapped_payload;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = after_null_block;
     incoming_blocks[1] = after_ok_block;
     IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
@@ -5966,7 +5934,7 @@ static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode
         }
         scope = scope->parent;
     }
-    TldVar *tld_var = allocate<TldVar>(1);
+    TldVar *tld_var = heap::c_allocator.create<TldVar>();
     init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base);
     tld_var->base.resolution = TldResolutionInvalid;
     tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false,
@@ -5983,7 +5951,7 @@ static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node,
     if (buf_eql_str(variable_name, "_")) {
         if (lval == LValPtr) {
             IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, node);
-            const_instruction->value = create_const_vals(1);
+            const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
             const_instruction->value->type = get_pointer_to_type(irb->codegen,
                     irb->codegen->builtin_types.entry_void, false);
             const_instruction->value->special = ConstValSpecialStatic;
@@ -6177,7 +6145,7 @@ static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *aw
         return fn_ref;
 
     size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset;
-    IrInstSrc **args = allocate<IrInstSrc*>(arg_count);
+    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
     for (size_t i = 0; i < arg_count; i += 1) {
         AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
         IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
@@ -6203,7 +6171,7 @@ static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstN
 
     IrInstSrc *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref);
 
-    IrInstSrc **args = allocate<IrInstSrc*>(args_len);
+    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len);
     for (size_t i = 0; i < args_len; i += 1) {
         AstNode *arg_node = args_ptr[i];
 
@@ -6388,7 +6356,7 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
             }
         case BuiltinFnIdCompileLog:
             {
-                IrInstSrc **args = allocate<IrInstSrc*>(actual_param_count);
+                IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(actual_param_count);
 
                 for (size_t i = 0; i < actual_param_count; i += 1) {
                     AstNode *arg_node = node->data.fn_call_expr.params.at(i);
@@ -7013,7 +6981,7 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
                 if (dest_type == irb->codegen->invalid_inst_src)
                     return dest_type;
 
-                ResultLocBitCast *result_loc_bit_cast = allocate<ResultLocBitCast>(1);
+                ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create<ResultLocBitCast>();
                 result_loc_bit_cast->base.id = ResultLocIdBitCast;
                 result_loc_bit_cast->base.source_instruction = dest_type;
                 result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const;
@@ -7166,7 +7134,7 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
 
                 size_t arg_count = node->data.fn_call_expr.params.length - 2;
 
-                IrInstSrc **args = allocate<IrInstSrc*>(arg_count);
+                IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
                 for (size_t i = 0; i < arg_count; i += 1) {
                     AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2);
                     args[i] = ir_gen_node(irb, arg_node, scope);
@@ -7595,10 +7563,10 @@ static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *
         ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
 
     ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = then_expr_result;
     incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = after_then_block;
     incoming_blocks[1] = after_else_block;
 
@@ -7799,7 +7767,7 @@ static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNod
     IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
             field_name, true);
 
-    ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
+    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
     result_loc_inst->base.id = ResultLocIdInstruction;
     result_loc_inst->base.source_instruction = field_ptr;
     ir_ref_instruction(field_ptr, irb->current_basic_block);
@@ -7875,7 +7843,7 @@ static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, As
                     nullptr);
 
             size_t field_count = container_init_expr->entries.length;
-            IrInstSrcContainerInitFieldsField *fields = allocate<IrInstSrcContainerInitFieldsField>(field_count);
+            IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate<IrInstSrcContainerInitFieldsField>(field_count);
             for (size_t i = 0; i < field_count; i += 1) {
                 AstNode *entry_node = container_init_expr->entries.at(i);
                 assert(entry_node->type == NodeTypeStructValueField);
@@ -7884,7 +7852,7 @@ static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, As
                 AstNode *expr_node = entry_node->data.struct_val_field.expr;
 
                 IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
-                ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
+                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
                 result_loc_inst->base.id = ResultLocIdInstruction;
                 result_loc_inst->base.source_instruction = field_ptr;
                 result_loc_inst->base.allow_write_through_const = true;
@@ -7914,14 +7882,14 @@ static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, As
             IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
                     nullptr);
 
-            IrInstSrc **result_locs = allocate<IrInstSrc *>(item_count);
+            IrInstSrc **result_locs = heap::c_allocator.allocate<IrInstSrc *>(item_count);
             for (size_t i = 0; i < item_count; i += 1) {
                 AstNode *expr_node = container_init_expr->entries.at(i);
 
                 IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
                 IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
                         elem_index, false, PtrLenSingle, init_array_type_source_node);
-                ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
+                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
                 result_loc_inst->base.id = ResultLocIdInstruction;
                 result_loc_inst->base.source_instruction = elem_ptr;
                 result_loc_inst->base.allow_write_through_const = true;
@@ -7947,7 +7915,7 @@ static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, As
 }
 
 static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) {
-    ResultLocVar *result_loc_var = allocate<ResultLocVar>(1);
+    ResultLocVar *result_loc_var = heap::c_allocator.create<ResultLocVar>();
     result_loc_var->base.id = ResultLocIdVar;
     result_loc_var->base.source_instruction = alloca;
     result_loc_var->base.allow_write_through_const = true;
@@ -7961,7 +7929,7 @@ static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloc
 static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
         ResultLoc *parent_result_loc)
 {
-    ResultLocCast *result_loc_cast = allocate<ResultLocCast>(1);
+    ResultLocCast *result_loc_cast = heap::c_allocator.create<ResultLocCast>();
     result_loc_cast->base.id = ResultLocIdCast;
     result_loc_cast->base.source_instruction = dest_type;
     result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const;
@@ -8804,9 +8772,9 @@ static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node
                                 nullptr, 0, is_volatile, true);
     }
 
-    IrInstSrc **input_list = allocate<IrInstSrc *>(asm_expr->input_list.length);
-    IrInstSrc **output_types = allocate<IrInstSrc *>(asm_expr->output_list.length);
-    ZigVar **output_vars = allocate<ZigVar *>(asm_expr->output_list.length);
+    IrInstSrc **input_list = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->input_list.length);
+    IrInstSrc **output_types = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->output_list.length);
+    ZigVar **output_vars = heap::c_allocator.allocate<ZigVar *>(asm_expr->output_list.length);
     size_t return_count = 0;
     if (!is_volatile && asm_expr->output_list.length == 0) {
         add_node_error(irb->codegen, node,
@@ -8940,10 +8908,10 @@ static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNo
         ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
 
     ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = then_expr_result;
     incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = after_then_block;
     incoming_blocks[1] = after_else_block;
 
@@ -9037,10 +9005,10 @@ static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
         ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
 
     ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = then_expr_result;
     incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = after_then_block;
     incoming_blocks[1] = after_else_block;
 
@@ -9133,7 +9101,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
 
     IrInstSrcSwitchElseVar *switch_else_var = nullptr;
 
-    ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
+    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
     peer_parent->base.id = ResultLocIdPeerParent;
     peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
     peer_parent->end_bb = end_block;
@@ -9295,7 +9263,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
         ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
 
         IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
-        IrInstSrc **items = allocate<IrInstSrc *>(prong_item_count);
+        IrInstSrc **items = heap::c_allocator.allocate<IrInstSrc *>(prong_item_count);
 
         for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
             AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
@@ -9677,10 +9645,10 @@ static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
     ir_build_br(irb, parent_scope, node, end_block, is_comptime);
 
     ir_set_cursor_at_end_and_append_block(irb, end_block);
-    IrInstSrc **incoming_values = allocate<IrInstSrc *>(2);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
     incoming_values[0] = err_result;
     incoming_values[1] = unwrapped_payload;
-    IrBasicBlockSrc **incoming_blocks = allocate<IrBasicBlockSrc *>(2, "IrBasicBlockSrc *");
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
     incoming_blocks[0] = after_err_block;
     incoming_blocks[1] = after_ok_block;
     IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
@@ -9747,7 +9715,7 @@ static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope,
         scan_decls(irb->codegen, child_scope, child_node);
     }
 
-    TldContainer *tld_container = allocate<TldContainer>(1);
+    TldContainer *tld_container = heap::c_allocator.create<TldContainer>();
     init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope);
     tld_container->type_entry = container_type;
     tld_container->decls_scope = child_scope;
@@ -9790,7 +9758,7 @@ static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigTyp
     }
 
     err_set_type->data.error_set.err_count = count;
-    err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(count);
+    err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(count);
 
     bool need_comma = false;
     for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
@@ -9837,7 +9805,7 @@ static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstN
     err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
     err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
     err_set_type->data.error_set.err_count = 1;
-    err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
+    err_set_type->data.error_set.errors = heap::c_allocator.create<ErrorTableEntry *>();
 
     err_set_type->data.error_set.errors[0] = err_entry;
 
@@ -9868,16 +9836,16 @@ static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, As
     err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
     err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
     err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
-    err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
+    err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(err_count);
 
     size_t errors_count = irb->codegen->errors_by_index.length + err_count;
-    ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
+    ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
 
     for (uint32_t i = 0; i < err_count; i += 1) {
         AstNode *field_node = node->data.err_set_decl.decls.at(i);
         AstNode *symbol_node = ast_field_to_symbol_node(field_node);
         Buf *err_name = symbol_node->data.symbol_expr.symbol;
-        ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
+        ErrorTableEntry *err = heap::c_allocator.create<ErrorTableEntry>();
         err->decl_node = field_node;
         buf_init_from_buf(&err->name, err_name);
 
@@ -9902,7 +9870,7 @@ static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, As
         }
         errors[err->value] = err;
     }
-    deallocate(errors, errors_count, "ErrorTableEntry *");
+    heap::c_allocator.deallocate(errors, errors_count);
     return ir_build_const_type(irb, parent_scope, node, err_set_type);
 }
 
@@ -9910,7 +9878,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
     assert(node->type == NodeTypeFnProto);
 
     size_t param_count = node->data.fn_proto.params.length;
-    IrInstSrc **param_types = allocate<IrInstSrc*>(param_count);
+    IrInstSrc **param_types = heap::c_allocator.allocate<IrInstSrc*>(param_count);
 
     bool is_var_args = false;
     for (size_t i = 0; i < param_count; i += 1) {
@@ -10191,7 +10159,7 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope
 }
 
 static ResultLoc *no_result_loc(void) {
-    ResultLocNone *result_loc_none = allocate<ResultLocNone>(1);
+    ResultLocNone *result_loc_none = heap::c_allocator.create<ResultLocNone>();
     result_loc_none->base.id = ResultLocIdNone;
     return &result_loc_none->base;
 }
@@ -10280,7 +10248,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_e
     if (!instr_is_unreachable(result)) {
         ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr));
         // no need for save_err_ret_addr because this cannot return error
-        ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
+        ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
         result_loc_ret->base.id = ResultLocIdReturn;
         ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
         ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base));
@@ -10372,7 +10340,7 @@ static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, Ast
     if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val)))
         return err;
     ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val);
-    copy_const_val(child_val, &tmp);
+    copy_const_val(codegen, child_val, &tmp);
     return ErrorNone;
 }
 
@@ -11522,7 +11490,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
         return set1;
     }
     size_t errors_count = ira->codegen->errors_by_index.length;
-    ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
+    ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
     populate_error_set_table(errors, set1);
     ZigList<ErrorTableEntry *> intersection_list = {};
 
@@ -11543,7 +11511,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
             buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));
         }
     }
-    deallocate(errors, errors_count, "ErrorTableEntry *");
+    heap::c_allocator.deallocate(errors, errors_count);
 
     err_set_type->data.error_set.err_count = intersection_list.length;
     err_set_type->data.error_set.errors = intersection_list.items;
@@ -11595,7 +11563,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                  actual_ptr_type->data.pointer.sentinel));
         if (!ok_null_term_ptrs) {
             result.id = ConstCastResultIdPtrSentinel;
-            result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
+            result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero<ConstCastPtrSentinel>(1);
             result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
             result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
             return result;
@@ -11611,7 +11579,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile);
         if (!ok_cv_qualifiers) {
             result.id = ConstCastResultIdCV;
-            result.data.bad_cv = allocate_nonzero<ConstCastBadCV>(1);
+            result.data.bad_cv = heap::c_allocator.allocate_nonzero<ConstCastBadCV>(1);
             result.data.bad_cv->wanted_type = wanted_ptr_type;
             result.data.bad_cv->actual_type = actual_ptr_type;
             return result;
@@ -11623,7 +11591,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdPointerChild;
-            result.data.pointer_mismatch = allocate_nonzero<ConstCastPointerMismatch>(1);
+            result.data.pointer_mismatch = heap::c_allocator.allocate_nonzero<ConstCastPointerMismatch>(1);
             result.data.pointer_mismatch->child = child;
             result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
             result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
@@ -11634,7 +11602,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             (!wanted_allows_zero && !actual_allows_zero);
         if (!ok_allows_zero) {
             result.id = ConstCastResultIdBadAllowsZero;
-            result.data.bad_allows_zero = allocate_nonzero<ConstCastBadAllowsZero>(1);
+            result.data.bad_allows_zero = heap::c_allocator.allocate_nonzero<ConstCastBadAllowsZero>(1);
             result.data.bad_allows_zero->wanted_type = wanted_type;
             result.data.bad_allows_zero->actual_type = actual_type;
             return result;
@@ -11674,7 +11642,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdArrayChild;
-            result.data.array_mismatch = allocate_nonzero<ConstCastArrayMismatch>(1);
+            result.data.array_mismatch = heap::c_allocator.allocate_nonzero<ConstCastArrayMismatch>(1);
             result.data.array_mismatch->child = child;
             result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type;
             result.data.array_mismatch->actual_child = actual_type->data.array.child_type;
@@ -11685,7 +11653,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel));
         if (!ok_null_terminated) {
             result.id = ConstCastResultIdSentinelArrays;
-            result.data.sentinel_arrays = allocate_nonzero<ConstCastBadNullTermArrays>(1);
+            result.data.sentinel_arrays = heap::c_allocator.allocate_nonzero<ConstCastBadNullTermArrays>(1);
             result.data.sentinel_arrays->child = child;
             result.data.sentinel_arrays->wanted_type = wanted_type;
             result.data.sentinel_arrays->actual_type = actual_type;
@@ -11713,7 +11681,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                  actual_ptr_type->data.pointer.sentinel));
         if (!ok_sentinels) {
             result.id = ConstCastResultIdPtrSentinel;
-            result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
+            result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero<ConstCastPtrSentinel>(1);
             result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
             result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
             return result;
@@ -11730,7 +11698,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                 return child;
             if (child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdSliceChild;
-                result.data.slice_mismatch = allocate_nonzero<ConstCastSliceMismatch>(1);
+                result.data.slice_mismatch = heap::c_allocator.allocate_nonzero<ConstCastSliceMismatch>(1);
                 result.data.slice_mismatch->child = child;
                 result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
                 result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
@@ -11747,7 +11715,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdOptionalChild;
-            result.data.optional = allocate_nonzero<ConstCastOptionalMismatch>(1);
+            result.data.optional = heap::c_allocator.allocate_nonzero<ConstCastOptionalMismatch>(1);
             result.data.optional->child = child;
             result.data.optional->wanted_child = wanted_type->data.maybe.child_type;
             result.data.optional->actual_child = actual_type->data.maybe.child_type;
@@ -11763,7 +11731,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             return payload_child;
         if (payload_child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdErrorUnionPayload;
-            result.data.error_union_payload = allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1);
+            result.data.error_union_payload = heap::c_allocator.allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1);
             result.data.error_union_payload->child = payload_child;
             result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type;
             result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type;
@@ -11775,7 +11743,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             return error_set_child;
         if (error_set_child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdErrorUnionErrorSet;
-            result.data.error_union_error_set = allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1);
+            result.data.error_union_error_set = heap::c_allocator.allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1);
             result.data.error_union_error_set->child = error_set_child;
             result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type;
             result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type;
@@ -11809,7 +11777,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
         }
 
         size_t errors_count = g->errors_by_index.length;
-        ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
+        ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
         for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
             ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
             assert(errors[error_entry->value] == nullptr);
@@ -11821,12 +11789,12 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             if (error_entry == nullptr) {
                 if (result.id == ConstCastResultIdOk) {
                     result.id = ConstCastResultIdErrSet;
-                    result.data.error_set_mismatch = allocate<ConstCastErrSetMismatch>(1);
+                    result.data.error_set_mismatch = heap::c_allocator.create<ConstCastErrSetMismatch>();
                 }
                 result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
             }
         }
-        deallocate(errors, errors_count, "ErrorTableEntry *");
+        heap::c_allocator.deallocate(errors, errors_count);
         return result;
     }
 
@@ -11855,7 +11823,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                 return child;
             if (child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdFnReturnType;
-                result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
+                result.data.return_type = heap::c_allocator.allocate_nonzero<ConstCastOnly>(1);
                 *result.data.return_type = child;
                 return result;
             }
@@ -11884,7 +11852,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                 result.data.fn_arg.arg_index = i;
                 result.data.fn_arg.actual_param_type = actual_param_info->type;
                 result.data.fn_arg.expected_param_type = expected_param_info->type;
-                result.data.fn_arg.child = allocate_nonzero<ConstCastOnly>(1);
+                result.data.fn_arg.child = heap::c_allocator.allocate_nonzero<ConstCastOnly>(1);
                 *result.data.fn_arg.child = arg_child;
                 return result;
             }
@@ -11905,14 +11873,14 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
 
     if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) {
         result.id = ConstCastResultIdIntShorten;
-        result.data.int_shorten = allocate_nonzero<ConstCastIntShorten>(1);
+        result.data.int_shorten = heap::c_allocator.allocate_nonzero<ConstCastIntShorten>(1);
         result.data.int_shorten->wanted_type = wanted_type;
         result.data.int_shorten->actual_type = actual_type;
         return result;
     }
 
     result.id = ConstCastResultIdType;
-    result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
+    result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1);
     result.data.type_mismatch->wanted_type = wanted_type;
     result.data.type_mismatch->actual_type = actual_type;
     return result;
@@ -11921,7 +11889,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
 static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
     size_t old_errors_count = *errors_count;
     *errors_count = g->errors_by_index.length;
-    *errors = reallocate(*errors, old_errors_count, *errors_count);
+    *errors = heap::c_allocator.reallocate(*errors, old_errors_count, *errors_count);
 }
 
 static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type,
@@ -12591,7 +12559,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    free(errors);
+    heap::c_allocator.deallocate(errors, errors_count);
 
     if (convert_to_const_slice) {
         if (prev_inst->value->type->id == ZigTypeIdPointer) {
@@ -12670,7 +12638,7 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInst *source_instr,
         case CastOpBitCast:
             zig_panic("TODO");
         case CastOpNoop: {
-            copy_const_val(const_val, other_val);
+            copy_const_val(ira->codegen, const_val, other_val);
             const_val->type = new_type;
             break;
         }
@@ -13199,7 +13167,7 @@ Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
     if (type_is_invalid(return_ptr->type))
         return ErrorSemanticAnalyzeFail;
 
-    IrExecutableSrc *ir_executable = allocate<IrExecutableSrc>(1, "IrExecutableSrc");
+    IrExecutableSrc *ir_executable = heap::c_allocator.create<IrExecutableSrc>();
     ir_executable->source_node = source_node;
     ir_executable->parent_exec = parent_exec;
     ir_executable->name = exec_name;
@@ -13223,7 +13191,7 @@ Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
         ir_print_src(codegen, stderr, ir_executable, 2);
         fprintf(stderr, "}\n");
     }
-    IrExecutableGen *analyzed_executable = allocate<IrExecutableGen>(1, "IrExecutableGen");
+    IrExecutableGen *analyzed_executable = heap::c_allocator.create<IrExecutableGen>();
     analyzed_executable->source_node = source_node;
     analyzed_executable->parent_exec = parent_exec;
     analyzed_executable->source_exec = ir_executable;
@@ -13424,7 +13392,7 @@ static IrInstGen *ir_analyze_optional_wrap(IrAnalyze *ira, IrInst* source_instr,
                 source_instr->scope, source_instr->source_node);
         const_instruction->base.value->special = ConstValSpecialStatic;
         if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) {
-            copy_const_val(const_instruction->base.value, val);
+            copy_const_val(ira->codegen, const_instruction->base.value, val);
         } else {
             const_instruction->base.value->data.x_optional = val;
         }
@@ -13465,7 +13433,7 @@ static IrInstGen *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInst* source_ins
         if (val == nullptr)
             return ira->codegen->invalid_inst_gen;
 
-        ZigValue *err_set_val = create_const_vals(1);
+        ZigValue *err_set_val = ira->codegen->pass1_arena->create<ZigValue>();
         err_set_val->type = err_set_type;
         err_set_val->special = ConstValSpecialStatic;
         err_set_val->data.x_err_set = nullptr;
@@ -13577,7 +13545,7 @@ static IrInstGen *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInst* source_instr,
         if (!val)
             return ira->codegen->invalid_inst_gen;
 
-        ZigValue *err_set_val = create_const_vals(1);
+        ZigValue *err_set_val = ira->codegen->pass1_arena->create<ZigValue>();
         err_set_val->special = ConstValSpecialStatic;
         err_set_val->type = wanted_type->data.error_union.err_set_type;
         err_set_val->data.x_err_set = val->data.x_err_set;
@@ -13842,7 +13810,7 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
         result->value->special = ConstValSpecialStatic;
         result->value->type = wanted_type;
         bigint_init_bigint(&result->value->data.x_union.tag, &val->data.x_enum_tag);
-        result->value->data.x_union.payload = create_const_vals(1);
+        result->value->data.x_union.payload = ira->codegen->pass1_arena->create<ZigValue>();
         result->value->data.x_union.payload->special = ConstValSpecialStatic;
         result->value->data.x_union.payload->type = field_type;
         return result;
@@ -14147,7 +14115,7 @@ static IrInstGen *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInst* source_instr,
         if (pointee == nullptr)
             return ira->codegen->invalid_inst_gen;
         if (pointee->special != ConstValSpecialRuntime) {
-            ZigValue *array_val = create_const_vals(1);
+            ZigValue *array_val = ira->codegen->pass1_arena->create<ZigValue>();
             array_val->special = ConstValSpecialStatic;
             array_val->type = array_type;
             array_val->data.x_array.special = ConstArraySpecialNone;
@@ -14361,7 +14329,7 @@ static IrInstGen *ir_analyze_array_to_vector(IrAnalyze *ira, IrInst* source_inst
     if (instr_is_comptime(array)) {
         // arrays and vectors have the same ZigValue representation
         IrInstGen *result = ir_const(ira, source_instr, vector_type);
-        copy_const_val(result->value, array->value);
+        copy_const_val(ira->codegen, result->value, array->value);
         result->value->type = vector_type;
         return result;
     }
@@ -14374,7 +14342,7 @@ static IrInstGen *ir_analyze_vector_to_array(IrAnalyze *ira, IrInst* source_inst
     if (instr_is_comptime(vector)) {
         // arrays and vectors have the same ZigValue representation
         IrInstGen *result = ir_const(ira, source_instr, array_type);
-        copy_const_val(result->value, vector->value);
+        copy_const_val(ira->codegen, result->value, vector->value);
         result->value->type = array_type;
         return result;
     }
@@ -14674,7 +14642,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
             if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) {
                 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
                 if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) {
-                    copy_const_val(result->value, value->value);
+                    copy_const_val(ira->codegen, result->value, value->value);
                     result->value->type = wanted_type;
                 } else {
                     float_init_bigint(&result->value->data.x_bigint, value->value);
@@ -16507,7 +16475,7 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i
         IrInstGen *result = ir_const(ira, &bin_op_instruction->base.base,
             get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool));
         result->value->data.x_array.data.s_none.elements =
-            create_const_vals(resolved_type->data.vector.len);
+            ira->codegen->pass1_arena->allocate<ZigValue>(resolved_type->data.vector.len);
 
         expand_undef_array(ira->codegen, result->value);
         for (size_t i = 0;i < resolved_type->data.vector.len;i++) {
@@ -16515,7 +16483,7 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i
                 &op1_val->data.x_array.data.s_none.elements[i],
                 &op2_val->data.x_array.data.s_none.elements[i],
                 bin_op_instruction, op_id, one_possible_value);
-            copy_const_val(&result->value->data.x_array.data.s_none.elements[i], cur_res->value);
+            copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value);
         }
         return result;
     }
@@ -17415,7 +17383,7 @@ static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instructi
     ZigValue *out_array_val;
     size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index);
     if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) {
-        out_array_val = create_const_vals(1);
+        out_array_val = ira->codegen->pass1_arena->create<ZigValue>();
         out_array_val->special = ConstValSpecialStatic;
         out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel);
 
@@ -17427,11 +17395,11 @@ static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instructi
                 true, false, PtrLenUnknown, 0, 0, 0, false,
                 VECTOR_INDEX_NONE, nullptr, sentinel);
         result->value->type = get_slice_type(ira->codegen, ptr_type);
-        out_array_val = create_const_vals(1);
+        out_array_val = ira->codegen->pass1_arena->create<ZigValue>();
         out_array_val->special = ConstValSpecialStatic;
         out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel);
 
-        out_val->data.x_struct.fields = alloc_const_vals_ptrs(2);
+        out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2);
 
         out_val->data.x_struct.fields[slice_ptr_index]->type = ptr_type;
         out_val->data.x_struct.fields[slice_ptr_index]->special = ConstValSpecialStatic;
@@ -17448,7 +17416,7 @@ static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instructi
     } else {
         result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown,
                 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel);
-        out_array_val = create_const_vals(1);
+        out_array_val = ira->codegen->pass1_arena->create<ZigValue>();
         out_array_val->special = ConstValSpecialStatic;
         out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel);
         out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
@@ -17464,7 +17432,7 @@ static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instructi
     }
 
     uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0);
-    out_array_val->data.x_array.data.s_none.elements = create_const_vals(full_len);
+    out_array_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(full_len);
     // TODO handle the buf case here for an optimization
     expand_undef_array(ira->codegen, op1_array_val);
     expand_undef_array(ira->codegen, op2_array_val);
@@ -17472,21 +17440,21 @@ static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instructi
     size_t next_index = 0;
     for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
         ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
-        copy_const_val(elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]);
+        copy_const_val(ira->codegen, elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]);
         elem_dest_val->parent.id = ConstParentIdArray;
         elem_dest_val->parent.data.p_array.array_val = out_array_val;
         elem_dest_val->parent.data.p_array.elem_index = next_index;
     }
     for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
         ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
-        copy_const_val(elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]);
+        copy_const_val(ira->codegen, elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]);
         elem_dest_val->parent.id = ConstParentIdArray;
         elem_dest_val->parent.data.p_array.array_val = out_array_val;
         elem_dest_val->parent.data.p_array.elem_index = next_index;
     }
     if (next_index < full_len) {
         ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index];
-        copy_const_val(elem_dest_val, sentinel);
+        copy_const_val(ira->codegen, elem_dest_val, sentinel);
         elem_dest_val->parent.id = ConstParentIdArray;
         elem_dest_val->parent.data.p_array.array_val = out_array_val;
         elem_dest_val->parent.data.p_array.elem_index = next_index;
@@ -17565,13 +17533,13 @@ static IrInstGen *ir_analyze_array_mult(IrAnalyze *ira, IrInstSrcBinOp *instruct
         // TODO optimize the buf case
         expand_undef_array(ira->codegen, array_val);
         size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0;
-        out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len + extra_null_term);
+        out_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(new_array_len + extra_null_term);
 
         uint64_t i = 0;
         for (uint64_t x = 0; x < mult_amt; x += 1) {
             for (uint64_t y = 0; y < old_array_len; y += 1) {
                 ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
-                copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]);
+                copy_const_val(ira->codegen, elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]);
                 elem_dest_val->parent.id = ConstParentIdArray;
                 elem_dest_val->parent.data.p_array.array_val = out_val;
                 elem_dest_val->parent.data.p_array.elem_index = i;
@@ -17582,7 +17550,7 @@ static IrInstGen *ir_analyze_array_mult(IrAnalyze *ira, IrInstSrcBinOp *instruct
 
         if (array_type->data.array.sentinel != nullptr) {
             ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
-            copy_const_val(elem_dest_val, array_type->data.array.sentinel);
+            copy_const_val(ira->codegen, elem_dest_val, array_type->data.array.sentinel);
             elem_dest_val->parent.id = ConstParentIdArray;
             elem_dest_val->parent.data.p_array.array_val = out_val;
             elem_dest_val->parent.data.p_array.elem_index = i;
@@ -17623,14 +17591,14 @@ static IrInstGen *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,
     }
 
     size_t errors_count = ira->codegen->errors_by_index.length;
-    ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
+    ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
     for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
         ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
         assert(errors[error_entry->value] == nullptr);
         errors[error_entry->value] = error_entry;
     }
     ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name);
-    deallocate(errors, errors_count, "ErrorTableEntry *");
+    heap::c_allocator.deallocate(errors, errors_count);
 
     return ir_const_type(ira, &instruction->base.base, result_type);
 }
@@ -17729,8 +17697,8 @@ static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclV
             if (var->gen_is_const) {
                 var->const_value = init_val;
             } else {
-                var->const_value = create_const_vals(1);
-                copy_const_val(var->const_value, init_val);
+                var->const_value = ira->codegen->pass1_arena->create<ZigValue>();
+                copy_const_val(ira->codegen, var->const_value, init_val);
             }
         }
     }
@@ -17904,7 +17872,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
     // It's not clear how all the different types are supposed to be handled.
     // Need comprehensive tests for exporting one thing in one file and declaring an extern var
     // in another file.
-    TldFn *tld_fn = allocate<TldFn>(1);
+    TldFn *tld_fn = heap::c_allocator.create<TldFn>();
     tld_fn->base.id = TldIdFn;
     tld_fn->base.source_node = instruction->base.base.source_node;
 
@@ -18133,7 +18101,7 @@ static IrInstGen *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstSrcEr
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1, "LazyValueErrUnionType");
+    LazyValueErrUnionType *lazy_err_union_type = heap::c_allocator.create<LazyValueErrUnionType>();
     lazy_err_union_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_err_union_type->base;
     lazy_err_union_type->base.id = LazyValueIdErrUnionType;
@@ -18154,7 +18122,7 @@ static IrInstGen *ir_analyze_alloca(IrAnalyze *ira, IrInst *source_inst, ZigType
 {
     Error err;
 
-    ZigValue *pointee = create_const_vals(1);
+    ZigValue *pointee = ira->codegen->pass1_arena->create<ZigValue>();
     pointee->special = ConstValSpecialUndef;
     pointee->llvm_align = align;
 
@@ -18235,8 +18203,8 @@ static bool type_can_bit_cast(ZigType *t) {
     }
 }
 
-static void set_up_result_loc_for_inferred_comptime(IrInstGen *ptr) {
-    ZigValue *undef_child = create_const_vals(1);
+static void set_up_result_loc_for_inferred_comptime(IrAnalyze *ira, IrInstGen *ptr) {
+    ZigValue *undef_child = ira->codegen->pass1_arena->create<ZigValue>();
     undef_child->type = ptr->value->type->data.pointer.child_type;
     undef_child->special = ConstValSpecialUndef;
     ptr->value->special = ConstValSpecialStatic;
@@ -18282,7 +18250,7 @@ static IrInstGen *ir_resolve_no_result_loc(IrAnalyze *ira, IrInst *suspend_sourc
     IrInstGenAlloca *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, "");
     alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false,
             PtrLenSingle, 0, 0, 0, false);
-    set_up_result_loc_for_inferred_comptime(&alloca_gen->base);
+    set_up_result_loc_for_inferred_comptime(ira, &alloca_gen->base);
     ZigFn *fn_entry = ira->new_irb.exec->fn_entry;
     if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) {
         fn_entry->alloca_gen_list.append(alloca_gen);
@@ -18346,7 +18314,6 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
                 ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope,
                     buf_create_from_str(var->name), var->src_is_const, var->gen_is_const,
                     var->shadowable, var->is_comptime, true);
-                new_var->owner_exec = var->owner_exec;
                 new_var->align_bytes = var->align_bytes;
 
                 var->next_var = new_var;
@@ -18685,15 +18652,15 @@ static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr
                 if (!val)
                     return ira->codegen->invalid_inst_gen;
                 field->is_comptime = true;
-                field->init_val = create_const_vals(1);
-                copy_const_val(field->init_val, val);
+                field->init_val = ira->codegen->pass1_arena->create<ZigValue>();
+                copy_const_val(ira->codegen, field->init_val, val);
                 return result_loc;
             }
 
             ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false);
             if (instr_is_comptime(result_loc)) {
                 casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type);
-                copy_const_val(casted_ptr->value, result_loc->value);
+                copy_const_val(ira->codegen, casted_ptr->value, result_loc->value);
                 casted_ptr->value->type = struct_ptr_type;
             } else {
                 casted_ptr = result_loc;
@@ -18706,8 +18673,8 @@ static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr
                     ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val,
                             suspend_source_instr->source_node);
                     struct_val->special = ConstValSpecialStatic;
-                    struct_val->data.x_struct.fields = realloc_const_vals_ptrs(struct_val->data.x_struct.fields,
-                            old_field_count, new_field_count);
+                    struct_val->data.x_struct.fields = realloc_const_vals_ptrs(ira->codegen,
+                            struct_val->data.x_struct.fields, old_field_count, new_field_count);
 
                     ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count];
                     field_val->special = ConstValSpecialUndef;
@@ -19007,10 +18974,10 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
         if (!arg_val)
             return false;
     } else {
-        arg_val = create_const_runtime(casted_arg->value->type);
+        arg_val = create_const_runtime(ira->codegen, casted_arg->value->type);
     }
     if (arg_part_of_generic_id) {
-        copy_const_val(&generic_id->params[generic_id->param_count], arg_val);
+        copy_const_val(ira->codegen, &generic_id->params[generic_id->param_count], arg_val);
         generic_id->param_count += 1;
     }
 
@@ -19159,7 +19126,7 @@ static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,
                 if (dest_val == nullptr)
                     return ira->codegen->invalid_inst_gen;
                 if (dest_val->special != ConstValSpecialRuntime) {
-                    copy_const_val(dest_val, value->value);
+                    copy_const_val(ira->codegen, dest_val, value->value);
 
                     if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar &&
                         !ira->new_irb.current_basic_block->must_be_comptime_source_instr)
@@ -19394,8 +19361,6 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
             {
                 return ira->codegen->invalid_inst_gen;
             }
-            destroy(result_ptr, "ZigValue");
-            result_ptr = nullptr;
 
             if (inferred_err_set_type != nullptr) {
                 inferred_err_set_type->data.error_set.incomplete = false;
@@ -19403,7 +19368,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
                     ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set;
                     if (err != nullptr) {
                         inferred_err_set_type->data.error_set.err_count = 1;
-                        inferred_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
+                        inferred_err_set_type->data.error_set.errors = heap::c_allocator.create<ErrorTableEntry *>();
                         inferred_err_set_type->data.error_set.errors[0] = err;
                     }
                     ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type;
@@ -19437,12 +19402,12 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
 
         size_t new_fn_arg_count = first_arg_1_or_0 + args_len;
 
-        IrInstGen **casted_args = allocate<IrInstGen *>(new_fn_arg_count);
+        IrInstGen **casted_args = heap::c_allocator.allocate<IrInstGen *>(new_fn_arg_count);
 
         // Fork a scope of the function with known values for the parameters.
         Scope *parent_scope = fn_entry->fndef_scope->base.parent;
         ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node);
-        impl_fn->param_source_nodes = allocate<AstNode *>(new_fn_arg_count);
+        impl_fn->param_source_nodes = heap::c_allocator.allocate<AstNode *>(new_fn_arg_count);
         buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name);
         impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn);
         impl_fn->child_scope = &impl_fn->fndef_scope->base;
@@ -19453,10 +19418,10 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
 
         // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly
         // as the key in generic_table
-        GenericFnTypeId *generic_id = allocate<GenericFnTypeId>(1);
+        GenericFnTypeId *generic_id = heap::c_allocator.create<GenericFnTypeId>();
         generic_id->fn_entry = fn_entry;
         generic_id->param_count = 0;
-        generic_id->params = create_const_vals(new_fn_arg_count);
+        generic_id->params = ira->codegen->pass1_arena->allocate<ZigValue>(new_fn_arg_count);
         size_t next_proto_i = 0;
 
         if (first_arg_ptr) {
@@ -19516,7 +19481,6 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
             IrInstGenConst *const_instruction = ir_create_inst_noval<IrInstGenConst>(&ira->new_irb,
                     impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
             const_instruction->base.value = align_result;
-            destroy(result_ptr, "ZigValue");
 
             uint32_t align_bytes = 0;
             ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes);
@@ -19649,7 +19613,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
     }
 
 
-    IrInstGen **casted_args = allocate<IrInstGen *>(call_param_count);
+    IrInstGen **casted_args = heap::c_allocator.allocate<IrInstGen *>(call_param_count);
     size_t next_arg_index = 0;
     if (first_arg_ptr) {
         assert(first_arg_ptr->value->type->id == ZigTypeIdPointer);
@@ -19781,7 +19745,7 @@ static IrInstGen *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstSrcCall *call_ins
             return ira->codegen->invalid_inst_gen;
         new_stack_src = &call_instruction->new_stack->base;
     }
-    IrInstGen **args_ptr = allocate<IrInstGen *>(call_instruction->arg_count, "IrInstGen *");
+    IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(call_instruction->arg_count);
     for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
         args_ptr[i] = call_instruction->args[i]->child;
         if (type_is_invalid(args_ptr[i]->value->type))
@@ -19797,7 +19761,7 @@ static IrInstGen *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstSrcCall *call_ins
             first_arg_ptr, first_arg_ptr_src, modifier, new_stack, new_stack_src,
             call_instruction->is_async_call_builtin, args_ptr, call_instruction->arg_count, ret_ptr,
             call_instruction->result_loc);
-    deallocate(args_ptr, call_instruction->arg_count, "IrInstGen *");
+    heap::c_allocator.deallocate(args_ptr, call_instruction->arg_count);
     return result;
 }
 
@@ -19917,7 +19881,7 @@ static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCal
 
     if (is_tuple(args_type)) {
         args_len = args_type->data.structure.src_field_count;
-        args_ptr = allocate<IrInstGen *>(args_len, "IrInstGen *");
+        args_ptr = heap::c_allocator.allocate<IrInstGen *>(args_len);
         for (size_t i = 0; i < args_len; i += 1) {
             TypeStructField *arg_field = args_type->data.structure.fields[i];
             args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base.base, args, arg_field);
@@ -19930,12 +19894,12 @@ static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCal
     }
     IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,
             instruction->fn_ref, args_ptr, args_len, instruction->result_loc);
-    deallocate(args_ptr, args_len, "IrInstGen *");
+    heap::c_allocator.deallocate(args_ptr, args_len);
     return result;
 }
 
 static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) {
-    IrInstGen **args_ptr = allocate<IrInstGen *>(instruction->args_len, "IrInstGen *");
+    IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(instruction->args_len);
     for (size_t i = 0; i < instruction->args_len; i += 1) {
         args_ptr[i] = instruction->args_ptr[i]->child;
         if (type_is_invalid(args_ptr[i]->value->type))
@@ -19944,7 +19908,7 @@ static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCall
 
     IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,
             instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc);
-    deallocate(args_ptr, instruction->args_len, "IrInstGen *");
+    heap::c_allocator.deallocate(args_ptr, instruction->args_len);
     return result;
 }
 
@@ -20019,7 +19983,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
 
     if (dst_size <= src_size) {
         if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) {
-            copy_const_val(out_val, pointee);
+            copy_const_val(codegen, out_val, pointee);
             return ErrorNone;
         }
         Buf buf = BUF_INIT;
@@ -20087,7 +20051,7 @@ static IrInstGen *ir_analyze_optional_type(IrAnalyze *ira, IrInstSrcUnOp *instru
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1, "LazyValueOptType");
+    LazyValueOptType *lazy_opt_type = heap::c_allocator.create<LazyValueOptType>();
     lazy_opt_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_opt_type->base;
     lazy_opt_type->base.id = LazyValueIdOptType;
@@ -20371,7 +20335,7 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i
 
             if (value->value->special != ConstValSpecialRuntime) {
                 IrInstGen *result = ir_const(ira, &phi_instruction->base.base, nullptr);
-                copy_const_val(result->value, value->value);
+                copy_const_val(ira->codegen, result->value, value->value);
                 return result;
             } else {
                 return value;
@@ -20385,7 +20349,7 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i
         peer_parent->peers.length >= 2)
     {
         if (peer_parent->resolved_type == nullptr) {
-            IrInstGen **instructions = allocate<IrInstGen *>(peer_parent->peers.length);
+            IrInstGen **instructions = heap::c_allocator.allocate<IrInstGen *>(peer_parent->peers.length);
             for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
                 ResultLocPeer *this_peer = peer_parent->peers.at(i);
 
@@ -20758,7 +20722,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
             if (index == array_len && array_type->data.array.sentinel != nullptr) {
                 ZigType *elem_type = array_type->data.array.child_type;
                 IrInstGen *sentinel_elem = ir_const(ira, &elem_ptr_instruction->base.base, elem_type);
-                copy_const_val(sentinel_elem->value, array_type->data.array.sentinel);
+                copy_const_val(ira->codegen, sentinel_elem->value, array_type->data.array.sentinel);
                 return ir_get_ref(ira, &elem_ptr_instruction->base.base, sentinel_elem, true, false);
             }
             if (index >= array_len) {
@@ -20822,7 +20786,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
             {
                 if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) {
                     array_ptr_val->data.x_array.special = ConstArraySpecialNone;
-                    array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len);
+                    array_ptr_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(array_type->data.array.len);
                     array_ptr_val->special = ConstValSpecialStatic;
                     for (size_t i = 0; i < array_type->data.array.len; i += 1) {
                         ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i];
@@ -20845,11 +20809,11 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
                         return ira->codegen->invalid_inst_gen;
                     }
 
-                    ZigValue *array_init_val = create_const_vals(1);
+                    ZigValue *array_init_val = ira->codegen->pass1_arena->create<ZigValue>();
                     array_init_val->special = ConstValSpecialStatic;
                     array_init_val->type = actual_array_type;
                     array_init_val->data.x_array.special = ConstArraySpecialNone;
-                    array_init_val->data.x_array.data.s_none.elements = create_const_vals(actual_array_type->data.array.len);
+                    array_init_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(actual_array_type->data.array.len);
                     array_init_val->special = ConstValSpecialStatic;
                     for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) {
                         ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i];
@@ -21175,7 +21139,7 @@ static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_ins
     if (field->is_comptime) {
         IrInstGen *elem = ir_const(ira, source_instr, field_type);
         memoize_field_init_val(ira->codegen, struct_type, field);
-        copy_const_val(elem->value, field->init_val);
+        copy_const_val(ira->codegen, elem->value, field->init_val);
         return ir_get_ref2(ira, source_instr, elem, field_type, true, false);
     }
     switch (type_has_one_possible_value(ira->codegen, field_type)) {
@@ -21223,7 +21187,7 @@ static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_ins
             if (type_is_invalid(struct_val->type))
                 return ira->codegen->invalid_inst_gen;
             if (initializing && struct_val->special == ConstValSpecialUndef) {
-                struct_val->data.x_struct.fields = alloc_const_vals_ptrs(struct_type->data.structure.src_field_count);
+                struct_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, struct_type->data.structure.src_field_count);
                 struct_val->special = ConstValSpecialStatic;
                 for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
                     ZigValue *field_val = struct_val->data.x_struct.fields[i];
@@ -21265,7 +21229,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
     ZigType *container_ptr_type = container_ptr->value->type;
     ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr);
 
-    InferredStructField *inferred_struct_field = allocate<InferredStructField>(1, "InferredStructField");
+    InferredStructField *inferred_struct_field = heap::c_allocator.create<InferredStructField>();
     inferred_struct_field->inferred_struct_type = container_type;
     inferred_struct_field->field_name = field_name;
 
@@ -21285,7 +21249,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
         } else {
             result = ir_const(ira, source_instr, field_ptr_type);
         }
-        copy_const_val(result->value, ptr_val);
+        copy_const_val(ira->codegen, result->value, ptr_val);
         result->value->type = field_ptr_type;
         return result;
     }
@@ -21356,7 +21320,7 @@ static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name
                     return ira->codegen->invalid_inst_gen;
 
                 if (initializing) {
-                    ZigValue *payload_val = create_const_vals(1);
+                    ZigValue *payload_val = ira->codegen->pass1_arena->create<ZigValue>();
                     payload_val->special = ConstValSpecialUndef;
                     payload_val->type = field_type;
                     payload_val->parent.id = ConstParentIdUnion;
@@ -21539,7 +21503,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
         }
     } else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) {
         if (buf_eql_str(field_name, "len")) {
-            ZigValue *len_val = create_const_vals(1);
+            ZigValue *len_val = ira->codegen->pass1_arena->create<ZigValue>();
             if (container_type->id == ZigTypeIdPointer) {
                 init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len);
             } else {
@@ -21585,7 +21549,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                     bool ptr_is_const = true;
                     bool ptr_is_volatile = false;
                     return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                            create_const_enum(child_type, &field->value), child_type,
+                            create_const_enum(ira->codegen, child_type, &field->value), child_type,
                             ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
                 }
             }
@@ -21614,7 +21578,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                     bool ptr_is_const = true;
                     bool ptr_is_volatile = false;
                     return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                            create_const_enum(enum_type, &field->enum_field->value), enum_type,
+                            create_const_enum(ira->codegen, enum_type, &field->enum_field->value), enum_type,
                             ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
                 }
             }
@@ -21632,7 +21596,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                 if (existing_entry) {
                     err_entry = existing_entry->value;
                 } else {
-                    err_entry = allocate<ErrorTableEntry>(1);
+                    err_entry = heap::c_allocator.create<ErrorTableEntry>();
                     err_entry->decl_node = field_ptr_instruction->base.base.source_node;
                     buf_init_from_buf(&err_entry->name, field_name);
                     size_t error_value_count = ira->codegen->errors_by_index.length;
@@ -21659,7 +21623,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                 }
                 err_set_type = child_type;
             }
-            ZigValue *const_val = create_const_vals(1);
+            ZigValue *const_val = ira->codegen->pass1_arena->create<ZigValue>();
             const_val->special = ConstValSpecialStatic;
             const_val->type = err_set_type;
             const_val->data.x_err_set = err_entry;
@@ -21673,7 +21637,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                 bool ptr_is_const = true;
                 bool ptr_is_volatile = false;
                 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                    create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
+                    create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
                         child_type->data.integral.bit_count, false),
                     ira->codegen->builtin_types.entry_num_lit_int,
                     ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
@@ -21695,7 +21659,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                 bool ptr_is_const = true;
                 bool ptr_is_volatile = false;
                 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                    create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
+                    create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
                         child_type->data.floating.bit_count, false),
                     ira->codegen->builtin_types.entry_num_lit_int,
                     ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
@@ -21722,7 +21686,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                     return ira->codegen->invalid_inst_gen;
                 }
                 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                    create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
+                    create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
                         get_ptr_align(ira->codegen, child_type), false),
                     ira->codegen->builtin_types.entry_num_lit_int,
                     ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
@@ -21744,7 +21708,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
                 bool ptr_is_const = true;
                 bool ptr_is_volatile = false;
                 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
-                    create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
+                    create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
                         child_type->data.array.len, false),
                     ira->codegen->builtin_types.entry_num_lit_int,
                     ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
@@ -22024,7 +21988,7 @@ static IrInstGen *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstSrcSli
     IrInstGen *result = ir_const(ira, &slice_type_instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1, "LazyValueSliceType");
+    LazyValueSliceType *lazy_slice_type = heap::c_allocator.create<LazyValueSliceType>();
     lazy_slice_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_slice_type->base;
     lazy_slice_type->base.id = LazyValueIdSliceType;
@@ -22097,8 +22061,8 @@ static IrInstGen *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstSrcAsm *asm_i
 
     // TODO validate the output types and variable types
 
-    IrInstGen **input_list = allocate<IrInstGen *>(asm_expr->input_list.length);
-    IrInstGen **output_types = allocate<IrInstGen *>(asm_expr->output_list.length);
+    IrInstGen **input_list = heap::c_allocator.allocate<IrInstGen *>(asm_expr->input_list.length);
+    IrInstGen **output_types = heap::c_allocator.allocate<IrInstGen *>(asm_expr->output_list.length);
 
     ZigType *return_type = ira->codegen->builtin_types.entry_void;
     for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
@@ -22137,7 +22101,7 @@ static IrInstGen *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstSrcArr
     IrInstGen *result = ir_const(ira, &array_type_instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueArrayType *lazy_array_type = allocate<LazyValueArrayType>(1, "LazyValueArrayType");
+    LazyValueArrayType *lazy_array_type = heap::c_allocator.create<LazyValueArrayType>();
     lazy_array_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_array_type->base;
     lazy_array_type->base.id = LazyValueIdArrayType;
@@ -22162,7 +22126,7 @@ static IrInstGen *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstSrcSizeOf
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1, "LazyValueSizeOf");
+    LazyValueSizeOf *lazy_size_of = heap::c_allocator.create<LazyValueSizeOf>();
     lazy_size_of->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_size_of->base;
     lazy_size_of->base.id = LazyValueIdSizeOf;
@@ -22282,7 +22246,7 @@ static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* sou
                         return ira->codegen->invalid_inst_gen;
                     case OnePossibleValueNo:
                         if (!same_comptime_repr) {
-                            ZigValue *payload_val = create_const_vals(1);
+                            ZigValue *payload_val = ira->codegen->pass1_arena->create<ZigValue>();
                             payload_val->type = child_type;
                             payload_val->special = ConstValSpecialUndef;
                             payload_val->parent.id = ConstParentIdOptionalPayload;
@@ -22529,7 +22493,7 @@ static IrInstGen *ir_analyze_instruction_switch_br(IrAnalyze *ira,
         }
     }
 
-    IrInstGenSwitchBrCase *cases = allocate<IrInstGenSwitchBrCase>(case_count);
+    IrInstGenSwitchBrCase *cases = heap::c_allocator.allocate<IrInstGenSwitchBrCase>(case_count);
     for (size_t i = 0; i < case_count; i += 1) {
         IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i];
         IrInstGenSwitchBrCase *new_case = &cases[i];
@@ -22614,7 +22578,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
         case ZigTypeIdErrorSet: {
             if (pointee_val) {
                 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, nullptr);
-                copy_const_val(result->value, pointee_val);
+                copy_const_val(ira->codegen, result->value, pointee_val);
                 result->value->type = target_type;
                 return result;
             }
@@ -22834,7 +22798,7 @@ static IrInstGen *ir_analyze_instruction_switch_else_var(IrAnalyze *ira,
             return target_value_ptr;
         }
         // Make note of the errors handled by other cases
-        ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
+        ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
         // We may not have any case in the switch if this is a lone else
         const size_t switch_cases = instruction->switch_br ? instruction->switch_br->case_count : 0;
         for (size_t case_i = 0; case_i < switch_cases; case_i += 1) {
@@ -22870,7 +22834,7 @@ static IrInstGen *ir_analyze_instruction_switch_else_var(IrAnalyze *ira,
                 buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
             }
         }
-        free(errors);
+        heap::c_allocator.deallocate(errors, ira->codegen->errors_by_index.length);
 
         err_set_type->data.error_set.err_count = result_list.length;
         err_set_type->data.error_set.errors = result_list.items;
@@ -23018,7 +22982,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
 
     IrInstGen *first_non_const_instruction = nullptr;
 
-    AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
+    AstNode **field_assign_nodes = heap::c_allocator.allocate<AstNode *>(actual_field_count);
     ZigList<IrInstGen *> const_ptrs = {};
 
     bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope)
@@ -23089,7 +23053,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
             return ira->codegen->invalid_inst_gen;
 
         IrInstGen *runtime_inst = ir_const(ira, source_instr, field->init_val->type);
-        copy_const_val(runtime_inst->value, field->init_val);
+        copy_const_val(ira->codegen, runtime_inst->value, field->init_val);
 
         IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc,
                 container_type, true);
@@ -23353,7 +23317,7 @@ static IrInstGen *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstSrcErrNa
             err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true);
         }
         IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
-        copy_const_val(result->value, err->cached_error_name_val);
+        copy_const_val(ira->codegen, result->value, err->cached_error_name_val);
         result->value->type = str_type;
         return result;
     }
@@ -23679,11 +23643,11 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
         }
     }
 
-    ZigValue *declaration_array = create_const_vals(1);
+    ZigValue *declaration_array = ira->codegen->pass1_arena->create<ZigValue>();
     declaration_array->special = ConstValSpecialStatic;
     declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr);
     declaration_array->data.x_array.special = ConstArraySpecialNone;
-    declaration_array->data.x_array.data.s_none.elements = create_const_vals(declaration_count);
+    declaration_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(declaration_count);
     init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false);
 
     // Loop through the declarations and generate info.
@@ -23705,7 +23669,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
         declaration_val->special = ConstValSpecialStatic;
         declaration_val->type = type_info_declaration_type;
 
-        ZigValue **inner_fields = alloc_const_vals_ptrs(3);
+        ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3);
         ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee;
         init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(curr_entry->key), true);
         inner_fields[1]->special = ConstValSpecialStatic;
@@ -23736,7 +23700,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
                         // 1: Data.Var: type
                         bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 1);
 
-                        ZigValue *payload = create_const_vals(1);
+                        ZigValue *payload = ira->codegen->pass1_arena->create<ZigValue>();
                         payload->special = ConstValSpecialStatic;
                         payload->type = ira->codegen->builtin_types.entry_type;
                         payload->data.x_type = var->const_value->type;
@@ -23757,13 +23721,13 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
 
                     AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto;
 
-                    ZigValue *fn_decl_val = create_const_vals(1);
+                    ZigValue *fn_decl_val = ira->codegen->pass1_arena->create<ZigValue>();
                     fn_decl_val->special = ConstValSpecialStatic;
                     fn_decl_val->type = type_info_fn_decl_type;
                     fn_decl_val->parent.id = ConstParentIdUnion;
                     fn_decl_val->parent.data.p_union.union_val = inner_fields[2];
 
-                    ZigValue **fn_decl_fields = alloc_const_vals_ptrs(9);
+                    ZigValue **fn_decl_fields = alloc_const_vals_ptrs(ira->codegen, 9);
                     fn_decl_val->data.x_struct.fields = fn_decl_fields;
 
                     // fn_type: type
@@ -23801,7 +23765,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
                         0, 0, 0, false);
                     fn_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
                     if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) {
-                        fn_decl_fields[5]->data.x_optional = create_const_vals(1);
+                        fn_decl_fields[5]->data.x_optional = ira->codegen->pass1_arena->create<ZigValue>();
                         ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee;
                         init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0,
                                 buf_len(fn_node->lib_name), true);
@@ -23816,12 +23780,12 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
                     // arg_names: [][] const u8
                     ensure_field_index(fn_decl_val->type, "arg_names", 7);
                     size_t fn_arg_count = fn_entry->variable_list.length;
-                    ZigValue *fn_arg_name_array = create_const_vals(1);
+                    ZigValue *fn_arg_name_array = ira->codegen->pass1_arena->create<ZigValue>();
                     fn_arg_name_array->special = ConstValSpecialStatic;
                     fn_arg_name_array->type = get_array_type(ira->codegen,
                             get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr);
                     fn_arg_name_array->data.x_array.special = ConstArraySpecialNone;
-                    fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
+                    fn_arg_name_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(fn_arg_count);
 
                     init_const_slice(ira->codegen, fn_decl_fields[7], fn_arg_name_array, 0, fn_arg_count, false);
 
@@ -23848,7 +23812,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
                     // This is a type.
                     bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0);
 
-                    ZigValue *payload = create_const_vals(1);
+                    ZigValue *payload = ira->codegen->pass1_arena->create<ZigValue>();
                     payload->special = ConstValSpecialStatic;
                     payload->type = ira->codegen->builtin_types.entry_type;
                     payload->data.x_type = type_entry;
@@ -23914,11 +23878,11 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
     ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
     assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown));
 
-    ZigValue *result = create_const_vals(1);
+    ZigValue *result = ira->codegen->pass1_arena->create<ZigValue>();
     result->special = ConstValSpecialStatic;
     result->type = type_info_pointer_type;
 
-    ZigValue **fields = alloc_const_vals_ptrs(7);
+    ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7);
     result->data.x_struct.fields = fields;
 
     // size: Size
@@ -23973,7 +23937,7 @@ static void make_enum_field_val(IrAnalyze *ira, ZigValue *enum_field_val, TypeEn
     enum_field_val->special = ConstValSpecialStatic;
     enum_field_val->type = type_info_enum_field_type;
 
-    ZigValue **inner_fields = alloc_const_vals_ptrs(2);
+    ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);
     inner_fields[1]->special = ConstValSpecialStatic;
     inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
 
@@ -24019,11 +23983,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             break;
         case ZigTypeIdInt:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Int", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(2);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2);
                 result->data.x_struct.fields = fields;
 
                 // is_signed: bool
@@ -24041,11 +24005,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdFloat:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Float", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(1);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
                 result->data.x_struct.fields = fields;
 
                 // bits: u8
@@ -24065,11 +24029,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdArray:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Array", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(3);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3);
                 result->data.x_struct.fields = fields;
 
                 // len: usize
@@ -24089,11 +24053,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 break;
             }
         case ZigTypeIdVector: {
-            result = create_const_vals(1);
+            result = ira->codegen->pass1_arena->create<ZigValue>();
             result->special = ConstValSpecialStatic;
             result->type = ir_type_info_get_type(ira, "Vector", nullptr);
 
-            ZigValue **fields = alloc_const_vals_ptrs(2);
+            ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2);
             result->data.x_struct.fields = fields;
 
             // len: usize
@@ -24111,11 +24075,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
         }
         case ZigTypeIdOptional:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Optional", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(1);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
                 result->data.x_struct.fields = fields;
 
                 // child: type
@@ -24127,11 +24091,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 break;
             }
         case ZigTypeIdAnyFrame: {
-            result = create_const_vals(1);
+            result = ira->codegen->pass1_arena->create<ZigValue>();
             result->special = ConstValSpecialStatic;
             result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr);
 
-            ZigValue **fields = alloc_const_vals_ptrs(1);
+            ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
             result->data.x_struct.fields = fields;
 
             // child: ?type
@@ -24144,11 +24108,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
         }
         case ZigTypeIdEnum:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Enum", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(5);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5);
                 result->data.x_struct.fields = fields;
 
                 // layout: ContainerLayout
@@ -24170,11 +24134,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 }
                 uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
 
-                ZigValue *enum_field_array = create_const_vals(1);
+                ZigValue *enum_field_array = ira->codegen->pass1_arena->create<ZigValue>();
                 enum_field_array->special = ConstValSpecialStatic;
                 enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr);
                 enum_field_array->data.x_array.special = ConstArraySpecialNone;
-                enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count);
+                enum_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(enum_field_count);
 
                 init_const_slice(ira->codegen, fields[2], enum_field_array, 0, enum_field_count, false);
 
@@ -24204,7 +24168,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdErrorSet:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr);
 
@@ -24219,15 +24183,15 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) {
                     zig_unreachable();
                 }
-                ZigValue *slice_val = create_const_vals(1);
+                ZigValue *slice_val = ira->codegen->pass1_arena->create<ZigValue>();
                 result->data.x_optional = slice_val;
 
                 uint32_t error_count = type_entry->data.error_set.err_count;
-                ZigValue *error_array = create_const_vals(1);
+                ZigValue *error_array = ira->codegen->pass1_arena->create<ZigValue>();
                 error_array->special = ConstValSpecialStatic;
                 error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr);
                 error_array->data.x_array.special = ConstArraySpecialNone;
-                error_array->data.x_array.data.s_none.elements = create_const_vals(error_count);
+                error_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(error_count);
 
                 init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false);
                 for (uint32_t error_index = 0; error_index < error_count; error_index++) {
@@ -24237,7 +24201,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     error_val->special = ConstValSpecialStatic;
                     error_val->type = type_info_error_type;
 
-                    ZigValue **inner_fields = alloc_const_vals_ptrs(2);
+                    ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);
                     inner_fields[1]->special = ConstValSpecialStatic;
                     inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
 
@@ -24259,11 +24223,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdErrorUnion:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(2);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2);
                 result->data.x_struct.fields = fields;
 
                 // error_set: type
@@ -24282,11 +24246,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdUnion:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Union", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(4);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4);
                 result->data.x_struct.fields = fields;
 
                 // layout: ContainerLayout
@@ -24303,7 +24267,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 if (union_decl_node->data.container_decl.auto_enum ||
                     union_decl_node->data.container_decl.init_arg_expr != nullptr)
                 {
-                    ZigValue *tag_type = create_const_vals(1);
+                    ZigValue *tag_type = ira->codegen->pass1_arena->create<ZigValue>();
                     tag_type->special = ConstValSpecialStatic;
                     tag_type->type = ira->codegen->builtin_types.entry_type;
                     tag_type->data.x_type = type_entry->data.unionation.tag_type;
@@ -24319,11 +24283,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     zig_unreachable();
                 uint32_t union_field_count = type_entry->data.unionation.src_field_count;
 
-                ZigValue *union_field_array = create_const_vals(1);
+                ZigValue *union_field_array = ira->codegen->pass1_arena->create<ZigValue>();
                 union_field_array->special = ConstValSpecialStatic;
                 union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr);
                 union_field_array->data.x_array.special = ConstArraySpecialNone;
-                union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count);
+                union_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(union_field_count);
 
                 init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false);
 
@@ -24336,14 +24300,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     union_field_val->special = ConstValSpecialStatic;
                     union_field_val->type = type_info_union_field_type;
 
-                    ZigValue **inner_fields = alloc_const_vals_ptrs(3);
+                    ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3);
                     inner_fields[1]->special = ConstValSpecialStatic;
                     inner_fields[1]->type = get_optional_type(ira->codegen, type_info_enum_field_type);
 
                     if (fields[1]->data.x_optional == nullptr) {
                         inner_fields[1]->data.x_optional = nullptr;
                     } else {
-                        inner_fields[1]->data.x_optional = create_const_vals(1);
+                        inner_fields[1]->data.x_optional = ira->codegen->pass1_arena->create<ZigValue>();
                         make_enum_field_val(ira, inner_fields[1]->data.x_optional, union_field->enum_field, type_info_enum_field_type);
                     }
 
@@ -24378,11 +24342,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     break;
                 }
 
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(3);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3);
                 result->data.x_struct.fields = fields;
 
                 // layout: ContainerLayout
@@ -24399,11 +24363,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 }
                 uint32_t struct_field_count = type_entry->data.structure.src_field_count;
 
-                ZigValue *struct_field_array = create_const_vals(1);
+                ZigValue *struct_field_array = ira->codegen->pass1_arena->create<ZigValue>();
                 struct_field_array->special = ConstValSpecialStatic;
                 struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr);
                 struct_field_array->data.x_array.special = ConstArraySpecialNone;
-                struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count);
+                struct_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(struct_field_count);
 
                 init_const_slice(ira->codegen, fields[1], struct_field_array, 0, struct_field_count, false);
 
@@ -24414,7 +24378,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     struct_field_val->special = ConstValSpecialStatic;
                     struct_field_val->type = type_info_struct_field_type;
 
-                    ZigValue **inner_fields = alloc_const_vals_ptrs(4);
+                    ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4);
                     inner_fields[1]->special = ConstValSpecialStatic;
                     inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
 
@@ -24427,7 +24391,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                         inner_fields[1]->data.x_optional = nullptr;
                     } else {
                         size_t byte_offset = struct_field->offset;
-                        inner_fields[1]->data.x_optional = create_const_vals(1);
+                        inner_fields[1]->data.x_optional = ira->codegen->pass1_arena->create<ZigValue>();
                         inner_fields[1]->data.x_optional->special = ConstValSpecialStatic;
                         inner_fields[1]->data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int;
                         bigint_init_unsigned(&inner_fields[1]->data.x_optional->data.x_bigint, byte_offset);
@@ -24463,11 +24427,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
             }
         case ZigTypeIdFn:
             {
-                result = create_const_vals(1);
+                result = ira->codegen->pass1_arena->create<ZigValue>();
                 result->special = ConstValSpecialStatic;
                 result->type = ir_type_info_get_type(ira, "Fn", nullptr);
 
-                ZigValue **fields = alloc_const_vals_ptrs(5);
+                ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5);
                 result->data.x_struct.fields = fields;
 
                 // calling_convention: TypeInfo.CallingConvention
@@ -24494,7 +24458,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 if (type_entry->data.fn.fn_type_id.return_type == nullptr)
                     fields[3]->data.x_optional = nullptr;
                 else {
-                    ZigValue *return_type = create_const_vals(1);
+                    ZigValue *return_type = ira->codegen->pass1_arena->create<ZigValue>();
                     return_type->special = ConstValSpecialStatic;
                     return_type->type = ira->codegen->builtin_types.entry_type;
                     return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type;
@@ -24508,11 +24472,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                 size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
                         (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
 
-                ZigValue *fn_arg_array = create_const_vals(1);
+                ZigValue *fn_arg_array = ira->codegen->pass1_arena->create<ZigValue>();
                 fn_arg_array->special = ConstValSpecialStatic;
                 fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr);
                 fn_arg_array->data.x_array.special = ConstArraySpecialNone;
-                fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
+                fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(fn_arg_count);
 
                 init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false);
 
@@ -24526,7 +24490,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     bool arg_is_generic = fn_param_info->type == nullptr;
                     if (arg_is_generic) assert(is_generic);
 
-                    ZigValue **inner_fields = alloc_const_vals_ptrs(3);
+                    ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3);
                     inner_fields[0]->special = ConstValSpecialStatic;
                     inner_fields[0]->type = ira->codegen->builtin_types.entry_bool;
                     inner_fields[0]->data.x_bool = arg_is_generic;
@@ -24539,7 +24503,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
                     if (arg_is_generic)
                         inner_fields[2]->data.x_optional = nullptr;
                     else {
-                        ZigValue *arg_type = create_const_vals(1);
+                        ZigValue *arg_type = ira->codegen->pass1_arena->create<ZigValue>();
                         arg_type->special = ConstValSpecialStatic;
                         arg_type->type = ira->codegen->builtin_types.entry_type;
                         arg_type->data.x_type = fn_param_info->type;
@@ -24863,7 +24827,7 @@ static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcType
         type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry));
     }
     IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
-    copy_const_val(result->value, type_entry->cached_const_name_val);
+    copy_const_val(ira->codegen, result->value, type_entry->cached_const_name_val);
     return result;
 }
 
@@ -24895,7 +24859,6 @@ static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImpo
     }
     if (type_is_invalid(cimport_result->type))
         return ira->codegen->invalid_inst_gen;
-    destroy(result_ptr, "ZigValue");
 
     ZigPackage *cur_scope_pkg = scope_package(instruction->base.base.scope);
     Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize,
@@ -25574,11 +25537,11 @@ static IrInstGen *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstSrcToByt
             return ira->codegen->invalid_inst_gen;
 
         IrInstGen *result = ir_const(ira, &instruction->base.base, dest_slice_type);
-        result->value->data.x_struct.fields = alloc_const_vals_ptrs(2);
+        result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2);
 
         ZigValue *ptr_val = result->value->data.x_struct.fields[slice_ptr_index];
         ZigValue *target_ptr_val = target_val->data.x_struct.fields[slice_ptr_index];
-        copy_const_val(ptr_val, target_ptr_val);
+        copy_const_val(ira->codegen, ptr_val, target_ptr_val);
         ptr_val->type = dest_ptr_type;
 
         ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index];
@@ -25865,7 +25828,7 @@ static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr
         expand_undef_array(ira->codegen, b_val);
 
         IrInstGen *result = ir_const(ira, source_instr, result_type);
-        result->value->data.x_array.data.s_none.elements = create_const_vals(len_mask);
+        result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(len_mask);
         for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) {
             ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i];
             ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i];
@@ -25878,7 +25841,7 @@ static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr
             ZigValue *src_elem_val = (v >= 0) ?
                 &a->value->data.x_array.data.s_none.elements[v] :
                 &b->value->data.x_array.data.s_none.elements[~v];
-            copy_const_val(result_elem_val, src_elem_val);
+            copy_const_val(ira->codegen, result_elem_val, src_elem_val);
 
             ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr);
         }
@@ -25898,7 +25861,7 @@ static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr
 
         IrInstGen *expand_mask = ir_const(ira, &mask->base,
             get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32));
-        expand_mask->value->data.x_array.data.s_none.elements = create_const_vals(len_max);
+        expand_mask->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(len_max);
         uint32_t i = 0;
         for (; i < len_min; i += 1)
             bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i);
@@ -25968,9 +25931,9 @@ static IrInstGen *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstSrcSplat *i
             return ir_const_undef(ira, &instruction->base.base, return_type);
 
         IrInstGen *result = ir_const(ira, &instruction->base.base, return_type);
-        result->value->data.x_array.data.s_none.elements = create_const_vals(len_int);
+        result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(len_int);
         for (uint32_t i = 0; i < len_int; i += 1) {
-            copy_const_val(&result->value->data.x_array.data.s_none.elements[i], scalar_val);
+            copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], scalar_val);
         }
         return result;
     }
@@ -26108,7 +26071,7 @@ static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset
             }
 
             for (size_t i = start; i < end; i += 1) {
-                copy_const_val(&dest_elements[i], byte_val);
+                copy_const_val(ira->codegen, &dest_elements[i], byte_val);
             }
 
             return ir_const_void(ira, &instruction->base.base);
@@ -26284,7 +26247,7 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy
             // TODO check for noalias violations - this should be generalized to work for any function
 
             for (size_t i = 0; i < count; i += 1) {
-                copy_const_val(&dest_elements[dest_start + i], &src_elements[src_start + i]);
+                copy_const_val(ira->codegen, &dest_elements[dest_start + i], &src_elements[src_start + i]);
             }
 
             return ir_const_void(ira, &instruction->base.base);
@@ -26568,7 +26531,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
 
         IrInstGen *result = ir_const(ira, &instruction->base.base, return_type);
         ZigValue *out_val = result->value;
-        out_val->data.x_struct.fields = alloc_const_vals_ptrs(2);
+        out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2);
 
         ZigValue *ptr_val = out_val->data.x_struct.fields[slice_ptr_index];
 
@@ -26863,7 +26826,7 @@ static IrInstGen *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstSrcAlign
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1, "LazyValueAlignOf");
+    LazyValueAlignOf *lazy_align_of = heap::c_allocator.create<LazyValueAlignOf>();
     lazy_align_of->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_align_of->base;
     lazy_align_of->base.id = LazyValueIdAlignOf;
@@ -27189,7 +27152,7 @@ static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_inst
                 return ira->codegen->invalid_inst_gen;
 
             if (initializing && err_union_val->special == ConstValSpecialUndef) {
-                ZigValue *vals = create_const_vals(2);
+                ZigValue *vals = ira->codegen->pass1_arena->allocate<ZigValue>(2);
                 ZigValue *err_set_val = &vals[0];
                 ZigValue *payload_val = &vals[1];
 
@@ -27270,7 +27233,7 @@ static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source
             if (err_union_val == nullptr)
                 return ira->codegen->invalid_inst_gen;
             if (initializing && err_union_val->special == ConstValSpecialUndef) {
-                ZigValue *vals = create_const_vals(2);
+                ZigValue *vals = ira->codegen->pass1_arena->allocate<ZigValue>(2);
                 ZigValue *err_set_val = &vals[0];
                 ZigValue *payload_val = &vals[1];
 
@@ -27332,7 +27295,7 @@ static IrInstGen *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstSrcFnPro
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1, "LazyValueFnType");
+    LazyValueFnType *lazy_fn_type = heap::c_allocator.create<LazyValueFnType>();
     lazy_fn_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_fn_type->base;
     lazy_fn_type->base.id = LazyValueIdFnType;
@@ -27360,7 +27323,7 @@ static IrInstGen *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstSrcFnPro
 
     size_t param_count = proto_node->data.fn_proto.params.length;
     lazy_fn_type->proto_node = proto_node;
-    lazy_fn_type->param_types = allocate<IrInstGen *>(param_count);
+    lazy_fn_type->param_types = heap::c_allocator.allocate<IrInstGen *>(param_count);
 
     for (size_t param_index = 0; param_index < param_count; param_index += 1) {
         AstNode *param_node = proto_node->data.fn_proto.params.at(param_index);
@@ -27515,7 +27478,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
         }
 
         size_t field_prev_uses_count = ira->codegen->errors_by_index.length;
-        AstNode **field_prev_uses = allocate<AstNode *>(field_prev_uses_count, "AstNode *");
+        AstNode **field_prev_uses = heap::c_allocator.allocate<AstNode *>(field_prev_uses_count);
 
         for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
             IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i];
@@ -27572,7 +27535,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
             }
         }
 
-        deallocate(field_prev_uses, field_prev_uses_count, "AstNode *");
+        heap::c_allocator.deallocate(field_prev_uses, field_prev_uses_count);
     } else if (switch_type->id == ZigTypeIdInt) {
         RangeSet rs = {0};
         for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
@@ -27765,7 +27728,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
         }
 
         IrInstGen *result = ir_const(ira, &target->base, result_type);
-        copy_const_val(result->value, val);
+        copy_const_val(ira->codegen, result->value, val);
         result->value->type = result_type;
         return result;
     }
@@ -27861,7 +27824,7 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn
         InferredStructField *isf = (val->type->id == ZigTypeIdPointer) ?
             val->type->data.pointer.inferred_struct_field : nullptr;
         if (isf == nullptr) {
-            copy_const_val(result->value, val);
+            copy_const_val(ira->codegen, result->value, val);
         } else {
             // The destination value should have x_ptr struct pointing to underlying struct value
             result->value->data.x_ptr.mut = val->data.x_ptr.mut;
@@ -28018,7 +27981,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val)
                     while (gen_i < gen_field_count) {
                         size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
                         if (big_int_byte_count > child_buf_len) {
-                            child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
+                            child_buf = heap::c_allocator.allocate_nonzero<uint8_t>(big_int_byte_count);
                             child_buf_len = big_int_byte_count;
                         }
                         BigInt big_int;
@@ -28081,7 +28044,7 @@ static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNod
 
     switch (val->data.x_array.special) {
         case ConstArraySpecialNone:
-            val->data.x_array.data.s_none.elements = create_const_vals(len);
+            val->data.x_array.data.s_none.elements = codegen->pass1_arena->allocate<ZigValue>(len);
             for (size_t i = 0; i < len; i++) {
                 ZigValue *elem = &val->data.x_array.data.s_none.elements[i];
                 elem->special = ConstValSpecialStatic;
@@ -28167,7 +28130,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
                 }
                 case ContainerLayoutExtern: {
                     size_t src_field_count = val->type->data.structure.src_field_count;
-                    val->data.x_struct.fields = alloc_const_vals_ptrs(src_field_count);
+                    val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count);
                     for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
                         ZigValue *field_val = val->data.x_struct.fields[field_i];
                         field_val->special = ConstValSpecialStatic;
@@ -28184,7 +28147,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
                 }
                 case ContainerLayoutPacked: {
                     size_t src_field_count = val->type->data.structure.src_field_count;
-                    val->data.x_struct.fields = alloc_const_vals_ptrs(src_field_count);
+                    val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count);
                     size_t gen_field_count = val->type->data.structure.gen_field_count;
                     size_t gen_i = 0;
                     size_t src_i = 0;
@@ -28196,7 +28159,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
                     while (gen_i < gen_field_count) {
                         size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
                         if (big_int_byte_count > child_buf_len) {
-                            child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
+                            child_buf = heap::c_allocator.allocate_nonzero<uint8_t>(big_int_byte_count);
                             child_buf_len = big_int_byte_count;
                         }
                         BigInt big_int;
@@ -28306,7 +28269,7 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn
             return ira->codegen->invalid_inst_gen;
 
         IrInstGen *result = ir_const(ira, source_instr, dest_type);
-        uint8_t *buf = allocate_nonzero<uint8_t>(src_size_bytes);
+        uint8_t *buf = heap::c_allocator.allocate_nonzero<uint8_t>(src_size_bytes);
         buf_write_value_bytes(ira->codegen, buf, val);
         if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value)))
             return ira->codegen->invalid_inst_gen;
@@ -28448,7 +28411,7 @@ static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrTy
     IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
     result->value->special = ConstValSpecialLazy;
 
-    LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1, "LazyValuePtrType");
+    LazyValuePtrType *lazy_ptr_type = heap::c_allocator.create<LazyValuePtrType>();
     lazy_ptr_type->ira = ira; ira_ref(ira);
     result->value->data.x_lazy = &lazy_ptr_type->base;
     lazy_ptr_type->base.id = LazyValueIdPtrType;
@@ -29147,11 +29110,11 @@ static IrInstGen *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstSrcBswap *i
             return ir_const_undef(ira, &instruction->base.base, op_type);
 
         IrInstGen *result = ir_const(ira, &instruction->base.base, op_type);
-        size_t buf_size = int_type->data.integral.bit_count / 8;
-        uint8_t *buf = allocate_nonzero<uint8_t>(buf_size);
+        const size_t buf_size = int_type->data.integral.bit_count / 8;
+        uint8_t *buf = heap::c_allocator.allocate_nonzero<uint8_t>(buf_size);
         if (is_vector) {
             expand_undef_array(ira->codegen, val);
-            result->value->data.x_array.data.s_none.elements = create_const_vals(op_type->data.vector.len);
+            result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(op_type->data.vector.len);
             for (unsigned i = 0; i < op_type->data.vector.len; i += 1) {
                 ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i];
                 if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.base.source_node,
@@ -29175,7 +29138,7 @@ static IrInstGen *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstSrcBswap *i
             bigint_read_twos_complement(&result->value->data.x_bigint, buf, int_type->data.integral.bit_count, false,
                     int_type->data.integral.is_signed);
         }
-        free(buf);
+        heap::c_allocator.deallocate(buf, buf_size);
         return result;
     }
 
@@ -29207,8 +29170,8 @@ static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBi
         IrInstGen *result = ir_const(ira, &instruction->base.base, int_type);
         size_t num_bits = int_type->data.integral.bit_count;
         size_t buf_size = (num_bits + 7) / 8;
-        uint8_t *comptime_buf = allocate_nonzero<uint8_t>(buf_size);
-        uint8_t *result_buf = allocate_nonzero<uint8_t>(buf_size);
+        uint8_t *comptime_buf = heap::c_allocator.allocate_nonzero<uint8_t>(buf_size);
+        uint8_t *result_buf = heap::c_allocator.allocate_nonzero<uint8_t>(buf_size);
         memset(comptime_buf,0,buf_size);
         memset(result_buf,0,buf_size);
 
@@ -29894,7 +29857,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutableSrc *old_exec, IrExecutableGen
     assert(old_exec->first_err_trace_msg == nullptr);
     assert(expected_type == nullptr || !type_is_invalid(expected_type));
 
-    IrAnalyze *ira = allocate<IrAnalyze>(1, "IrAnalyze");
+    IrAnalyze *ira = heap::c_allocator.create<IrAnalyze>();
     ira->ref_count = 1;
     old_exec->analysis = ira;
     ira->codegen = codegen;
src/ir.hpp
@@ -37,6 +37,4 @@ ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_va
 void dbg_ir_break(const char *src_file, uint32_t line);
 void dbg_ir_clear(void);
 
-void destroy_instruction_gen(IrInstGen *inst);
-
 #endif
src/link.cpp
@@ -650,7 +650,7 @@ static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress
     };
     ZigList<CFile *> c_source_files = {0};
     for (size_t i = 0; i < array_length(unwind_src); i += 1) {
-        CFile *c_file = allocate<CFile>(1);
+        CFile *c_file = heap::c_allocator.create<CFile>();
         c_file->source_path = path_from_libunwind(parent, unwind_src[i].path);
         switch (unwind_src[i].kind) {
             case SrcC:
@@ -1111,7 +1111,7 @@ static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node
         Buf *full_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "%s",
                 buf_ptr(parent->zig_lib_dir), buf_ptr(src_file));
 
-        CFile *c_file = allocate<CFile>(1);
+        CFile *c_file = heap::c_allocator.create<CFile>();
         c_file->source_path = buf_ptr(full_path);
 
         musl_add_cc_args(parent, c_file, src_kind == MuslSrcO3);
@@ -1127,7 +1127,7 @@ static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node
 }
 
 static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
-    CFile *c_file = allocate<CFile>(1);
+    CFile *c_file = heap::c_allocator.create<CFile>();
     c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s",
             buf_ptr(parent->zig_lib_dir), src_path));
     c_file->args.append("-DHAVE_CONFIG_H");
@@ -1151,7 +1151,7 @@ static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *s
 }
 
 static void add_mingwex_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
-    CFile *c_file = allocate<CFile>(1);
+    CFile *c_file = heap::c_allocator.create<CFile>();
     c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s",
             buf_ptr(parent->zig_lib_dir), src_path));
     c_file->args.append("-DHAVE_CONFIG_H");
@@ -1178,7 +1178,7 @@ static void add_mingwex_os_dep(CodeGen *parent, CodeGen *child_gen, const char *
 static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2ProgressNode *progress_node) {
     if (parent->libc == nullptr && parent->zig_target->os == OsWindows) {
         if (strcmp(file, "crt2.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = buf_ptr(buf_sprintf(
                 "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtexe.c", buf_ptr(parent->zig_lib_dir)));
             mingw_add_cc_args(parent, c_file);
@@ -1190,7 +1190,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
             //c_file->args.append("-DWPRFLAG=1");
             return build_libc_object(parent, "crt2", c_file, progress_node);
         } else if (strcmp(file, "dllcrt2.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = buf_ptr(buf_sprintf(
                 "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtdll.c", buf_ptr(parent->zig_lib_dir)));
             mingw_add_cc_args(parent, c_file);
@@ -1231,7 +1231,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                 "mingw" OS_SEP "crt" OS_SEP "cxa_atexit.c",
             };
             for (size_t i = 0; i < array_length(deps); i += 1) {
-                CFile *c_file = allocate<CFile>(1);
+                CFile *c_file = heap::c_allocator.create<CFile>();
                 c_file->source_path = path_from_libc(parent, deps[i]);
                 c_file->args.append("-DHAVE_CONFIG_H");
                 c_file->args.append("-D_SYSCRT=1");
@@ -1301,7 +1301,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
         }
     } else if (parent->libc == nullptr && target_is_glibc(parent->zig_target)) {
         if (strcmp(file, "crti.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = glibc_start_asm_path(parent, "crti.S");
             glibc_add_include_dirs(parent, c_file);
             c_file->args.append("-D_LIBC_REENTRANT");
@@ -1317,7 +1317,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
             c_file->args.append("-Wa,--noexecstack");
             return build_libc_object(parent, "crti", c_file, progress_node);
         } else if (strcmp(file, "crtn.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = glibc_start_asm_path(parent, "crtn.S");
             glibc_add_include_dirs(parent, c_file);
             c_file->args.append("-D_LIBC_REENTRANT");
@@ -1328,7 +1328,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
             c_file->args.append("-Wa,--noexecstack");
             return build_libc_object(parent, "crtn", c_file, progress_node);
         } else if (strcmp(file, "start.os") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = glibc_start_asm_path(parent, "start.S");
             glibc_add_include_dirs(parent, c_file);
             c_file->args.append("-D_LIBC_REENTRANT");
@@ -1346,7 +1346,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
             c_file->args.append("-Wa,--noexecstack");
             return build_libc_object(parent, "start", c_file, progress_node);
         } else if (strcmp(file, "abi-note.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "abi-note.S");
             c_file->args.append("-I");
             c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu"));
@@ -1369,7 +1369,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
         } else if (strcmp(file, "libc_nonshared.a") == 0) {
             CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node);
             {
-                CFile *c_file = allocate<CFile>(1);
+                CFile *c_file = heap::c_allocator.create<CFile>();
                 c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c");
                 c_file->args.append("-std=gnu11");
                 c_file->args.append("-fgnu89-inline");
@@ -1419,7 +1419,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                 {"stack_chk_fail_local", "glibc" OS_SEP "debug" OS_SEP "stack_chk_fail_local.c"},
             };
             for (size_t i = 0; i < array_length(deps); i += 1) {
-                CFile *c_file = allocate<CFile>(1);
+                CFile *c_file = heap::c_allocator.create<CFile>();
                 c_file->source_path = path_from_libc(parent, deps[i].path);
                 c_file->args.append("-std=gnu11");
                 c_file->args.append("-fgnu89-inline");
@@ -1451,26 +1451,26 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
         }
     } else if (parent->libc == nullptr && target_is_musl(parent->zig_target)) {
         if (strcmp(file, "crti.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = musl_start_asm_path(parent, "crti.s");
             musl_add_cc_args(parent, c_file, false);
             c_file->args.append("-Qunused-arguments");
             return build_libc_object(parent, "crti", c_file, progress_node);
         } else if (strcmp(file, "crtn.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = musl_start_asm_path(parent, "crtn.s");
             c_file->args.append("-Qunused-arguments");
             musl_add_cc_args(parent, c_file, false);
             return build_libc_object(parent, "crtn", c_file, progress_node);
         } else if (strcmp(file, "crt1.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "crt1.c");
             musl_add_cc_args(parent, c_file, false);
             c_file->args.append("-fno-stack-protector");
             c_file->args.append("-DCRT");
             return build_libc_object(parent, "crt1", c_file, progress_node);
         } else if (strcmp(file, "Scrt1.o") == 0) {
-            CFile *c_file = allocate<CFile>(1);
+            CFile *c_file = heap::c_allocator.create<CFile>();
             c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "Scrt1.c");
             musl_add_cc_args(parent, c_file, false);
             c_file->args.append("-fPIC");
@@ -1982,7 +1982,7 @@ static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_fi
     Buf *def_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "def-include",
             buf_ptr(parent->zig_lib_dir));
 
-    CacheHash *cache_hash = allocate<CacheHash>(1);
+    CacheHash *cache_hash = heap::c_allocator.create<CacheHash>();
     cache_init(cache_hash, manifest_dir);
 
     cache_buf(cache_hash, compiler_id);
@@ -2367,7 +2367,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
 
         lj->args.append(get_def_lib(g, name, &lib_path));
 
-        free(name);
+        mem::os::free(name);
     }
 }
 
src/list.hpp
@@ -13,7 +13,7 @@
 template<typename T>
 struct ZigList {
     void deinit() {
-        deallocate(items, capacity);
+        heap::c_allocator.deallocate(items, capacity);
     }
     void append(const T& item) {
         ensure_capacity(length + 1);
@@ -70,7 +70,7 @@ struct ZigList {
             better_capacity = better_capacity * 5 / 2 + 8;
         } while (better_capacity < new_capacity);
 
-        items = reallocate_nonzero(items, capacity, better_capacity);
+        items = heap::c_allocator.reallocate_nonzero(items, capacity, better_capacity);
         capacity = better_capacity;
     }
 
@@ -91,5 +91,3 @@ struct ZigList {
 };
 
 #endif
-
-
src/main.cpp
@@ -11,12 +11,14 @@
 #include "compiler.hpp"
 #include "config.h"
 #include "error.hpp"
+#include "heap.hpp"
 #include "os.hpp"
 #include "target.hpp"
 #include "libc_installation.hpp"
 #include "userland.h"
 #include "glibc.hpp"
 #include "dump_analysis.hpp"
+#include "mem_profile.hpp"
 
 #include <stdio.h>
 
@@ -243,21 +245,10 @@ int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
     if (root_progress_node != nullptr) {
         stage2_progress_end(root_progress_node);
     }
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    if (mem_report) {
-        memprof_dump_stats(stderr);
-    }
-#endif
     return exit_code;
 }
 
-int main(int argc, char **argv) {
-    stage2_attach_segfault_handler();
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_init();
-#endif
-
+static int main0(int argc, char **argv) {
     char *arg0 = argv[0];
     Error err;
 
@@ -278,9 +269,6 @@ int main(int argc, char **argv) {
         return ZigClang_main(argc, argv);
     }
 
-    // Must be before all os.hpp function calls.
-    os_init();
-
     if (argc == 2 && strcmp(argv[1], "id") == 0) {
         Buf *compiler_id;
         if ((err = get_compiler_id(&compiler_id))) {
@@ -439,7 +427,7 @@ int main(int argc, char **argv) {
     bool enable_doc_generation = false;
     bool disable_bin_generation = false;
     const char *cache_dir = nullptr;
-    CliPkg *cur_pkg = allocate<CliPkg>(1);
+    CliPkg *cur_pkg = heap::c_allocator.create<CliPkg>();
     BuildMode build_mode = BuildModeDebug;
     ZigList<const char *> test_exec_args = {0};
     int runtime_args_start = -1;
@@ -635,6 +623,7 @@ int main(int argc, char **argv) {
             } else if (strcmp(arg, "-fmem-report") == 0) {
 #ifdef ZIG_ENABLE_MEM_PROFILE
                 mem_report = true;
+                mem::report_print = true;
 #else
                 fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n");
                 return print_error_usage(arg0);
@@ -695,7 +684,7 @@ int main(int argc, char **argv) {
                     fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
                     return print_error_usage(arg0);
                 }
-                CliPkg *new_cur_pkg = allocate<CliPkg>(1);
+                CliPkg *new_cur_pkg = heap::c_allocator.create<CliPkg>();
                 i += 1;
                 new_cur_pkg->name = argv[i];
                 i += 1;
@@ -810,7 +799,7 @@ int main(int argc, char **argv) {
                 } else if (strcmp(arg, "--object") == 0) {
                     objects.append(argv[i]);
                 } else if (strcmp(arg, "--c-source") == 0) {
-                    CFile *c_file = allocate<CFile>(1);
+                    CFile *c_file = heap::c_allocator.create<CFile>();
                     for (;;) {
                         if (argv[i][0] == '-') {
                             c_file->args.append(argv[i]);
@@ -990,7 +979,7 @@ int main(int argc, char **argv) {
             }
         }
         if (target_is_glibc(&target)) {
-            target.glibc_version = allocate<ZigGLibCVersion>(1);
+            target.glibc_version = heap::c_allocator.create<ZigGLibCVersion>();
 
             if (target_glibc != nullptr) {
                 if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) {
@@ -1138,7 +1127,7 @@ int main(int argc, char **argv) {
             }
             ZigLibCInstallation *libc = nullptr;
             if (libc_txt != nullptr) {
-                libc = allocate<ZigLibCInstallation>(1);
+                libc = heap::c_allocator.create<ZigLibCInstallation>();
                 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
                     fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));
                     return main_exit(root_progress_node, EXIT_FAILURE);
@@ -1269,7 +1258,8 @@ int main(int argc, char **argv) {
 
                 if (cmd == CmdRun) {
 #ifdef ZIG_ENABLE_MEM_PROFILE
-                    memprof_dump_stats(stderr);
+                    if (mem::report_print)
+                        mem::print_report();
 #endif
 
                     const char *exec_path = buf_ptr(&g->output_file_path);
@@ -1384,4 +1374,20 @@ int main(int argc, char **argv) {
     case CmdNone:
         return print_full_usage(arg0, stderr, EXIT_FAILURE);
     }
+    zig_unreachable();
+}
+
+int main(int argc, char **argv) {
+    stage2_attach_segfault_handler();
+    os_init();
+    mem::init();
+
+    auto result = main0(argc, argv);
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+    if (mem::report_print)
+        mem::intern_counters.print_report();
+#endif
+    mem::deinit();
+    return result;
 }
src/mem.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "config.h"
+#include "mem.hpp"
+#include "mem_profile.hpp"
+#include "heap.hpp"
+
+namespace mem {
+
+void init() {
+    heap::bootstrap_allocator_state.init("heap::bootstrap_allocator");
+    heap::c_allocator_state.init("heap::c_allocator");
+}
+
+void deinit() {
+    heap::c_allocator_state.deinit();
+    heap::bootstrap_allocator_state.deinit();
+}
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void print_report(FILE *file) {
+    heap::c_allocator_state.print_report(file);
+    intern_counters.print_report(file);
+}
+#endif
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+bool report_print = false;
+FILE *report_file{nullptr};
+#endif
+
+} // namespace mem
src/mem.hpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_HPP
+#define ZIG_MEM_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "util_base.hpp"
+#include "mem_type_info.hpp"
+
+//
+// -- Memory Allocation General Notes --
+//
+// `heap::c_allocator` is the preferred general allocator.
+//
+// `heap::bootstrap_allocator` is an implementation detail for use
+// by allocators themselves when incidental heap may be required for
+// profiling and statistics. It breaks the infinite recursion cycle.
+//
+// `mem::os` contains a raw wrapper for system malloc API used in
+// preference to calling ::{malloc, free, calloc, realloc} directly.
+// This isolates usage and helps with audits:
+//
+//      mem::os::malloc
+//      mem::os::free
+//      mem::os::calloc
+//      mem::os::realloc
+//
+namespace mem {
+
+// initialize mem module before any use
+void init();
+
+// deinitialize mem module to free memory and print report
+void deinit();
+
+// isolate system/libc allocators
+namespace os {
+
+ATTRIBUTE_RETURNS_NOALIAS
+inline void *malloc(size_t size) {
+#ifndef NDEBUG
+    // make behavior when size == 0 portable
+    if (size == 0)
+        return nullptr;
+#endif
+    auto ptr = ::malloc(size);
+    if (ptr == nullptr)
+        zig_panic("allocation failed");
+    return ptr;
+}
+
+inline void free(void *ptr) {
+    ::free(ptr);
+}
+
+ATTRIBUTE_RETURNS_NOALIAS
+inline void *calloc(size_t count, size_t size) {
+#ifndef NDEBUG
+    // make behavior when size == 0 portable
+    if (count == 0 || size == 0)
+        return nullptr;
+#endif
+    auto ptr = ::calloc(count, size);
+    if (ptr == nullptr)
+        zig_panic("allocation failed");
+    return ptr;
+}
+
+inline void *realloc(void *old_ptr, size_t size) {
+#ifndef NDEBUG
+    // make behavior when size == 0 portable
+    if (old_ptr == nullptr && size == 0)
+        return nullptr;
+#endif
+    auto ptr = ::realloc(old_ptr, size);
+    if (ptr == nullptr)
+        zig_panic("allocation failed");
+    return ptr;
+}
+
+} // namespace os
+
+struct Allocator {
+    virtual void destruct(Allocator *allocator) = 0;
+
+    template <typename T> ATTRIBUTE_RETURNS_NOALIAS
+    T *allocate(size_t count) {
+        return reinterpret_cast<T *>(this->internal_allocate(TypeInfo::make<T>(), count));
+    }
+
+    template <typename T> ATTRIBUTE_RETURNS_NOALIAS
+    T *allocate_nonzero(size_t count) {
+        return reinterpret_cast<T *>(this->internal_allocate_nonzero(TypeInfo::make<T>(), count));
+    }
+
+    template <typename T>
+    T *reallocate(T *old_ptr, size_t old_count, size_t new_count) {
+        return reinterpret_cast<T *>(this->internal_reallocate(TypeInfo::make<T>(), old_ptr, old_count, new_count));
+    }
+
+    template <typename T>
+    T *reallocate_nonzero(T *old_ptr, size_t old_count, size_t new_count) {
+        return reinterpret_cast<T *>(this->internal_reallocate_nonzero(TypeInfo::make<T>(), old_ptr, old_count, new_count));
+    }
+
+    template<typename T>
+    void deallocate(T *ptr, size_t count) {
+        this->internal_deallocate(TypeInfo::make<T>(), ptr, count);
+    }
+
+    template<typename T>
+    T *create() {
+        return reinterpret_cast<T *>(this->internal_allocate(TypeInfo::make<T>(), 1));
+    }
+
+    template<typename T>
+    void destroy(T *ptr) {
+        this->internal_deallocate(TypeInfo::make<T>(), ptr, 1);
+    }
+
+protected:
+    ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate(const TypeInfo &info, size_t count) = 0;
+    ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate_nonzero(const TypeInfo &info, size_t count) = 0;
+    virtual void *internal_reallocate(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0;
+    virtual void *internal_reallocate_nonzero(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0;
+    virtual void internal_deallocate(const TypeInfo &info, void *ptr, size_t count) = 0;
+};
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void print_report(FILE *file = nullptr);
+
+// global memory report flag
+extern bool report_print;
+// global memory report default destination
+extern FILE *report_file;
+#endif
+
+} // namespace mem
+
+#endif
src/mem_hash_map.hpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_HASH_MAP_HPP
+#define ZIG_MEM_HASH_MAP_HPP
+
+#include "mem.hpp"
+
+namespace mem {
+
+template<typename K, typename V, uint32_t (*HashFunction)(K key), bool (*EqualFn)(K a, K b)>
+class HashMap {
+public:
+    void init(Allocator& allocator, int capacity) {
+        init_capacity(allocator, capacity);
+    }
+    void deinit(Allocator& allocator) {
+        allocator.deallocate(_entries, _capacity);
+    }
+
+    struct Entry {
+        K key;
+        V value;
+        bool used;
+        int distance_from_start_index;
+    };
+
+    void clear() {
+        for (int i = 0; i < _capacity; i += 1) {
+            _entries[i].used = false;
+        }
+        _size = 0;
+        _max_distance_from_start_index = 0;
+        _modification_count += 1;
+    }
+
+    int size() const {
+        return _size;
+    }
+
+    void put(Allocator& allocator, const K &key, const V &value) {
+        _modification_count += 1;
+        internal_put(key, value);
+
+        // if we get too full (60%), double the capacity
+        if (_size * 5 >= _capacity * 3) {
+            Entry *old_entries = _entries;
+            int old_capacity = _capacity;
+            init_capacity(allocator, _capacity * 2);
+            // dump all of the old elements into the new table
+            for (int i = 0; i < old_capacity; i += 1) {
+                Entry *old_entry = &old_entries[i];
+                if (old_entry->used)
+                    internal_put(old_entry->key, old_entry->value);
+            }
+            allocator.deallocate(old_entries, old_capacity);
+        }
+    }
+
+    Entry *put_unique(Allocator& allocator, const K &key, const V &value) {
+        // TODO make this more efficient
+        Entry *entry = internal_get(key);
+        if (entry)
+            return entry;
+        put(allocator, key, value);
+        return nullptr;
+    }
+
+    const V &get(const K &key) const {
+        Entry *entry = internal_get(key);
+        if (!entry)
+            zig_panic("key not found");
+        return entry->value;
+    }
+
+    Entry *maybe_get(const K &key) const {
+        return internal_get(key);
+    }
+
+    void maybe_remove(const K &key) {
+        if (maybe_get(key)) {
+            remove(key);
+        }
+    }
+
+    void remove(const K &key) {
+        _modification_count += 1;
+        int start_index = key_to_index(key);
+        for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
+            int index = (start_index + roll_over) % _capacity;
+            Entry *entry = &_entries[index];
+
+            if (!entry->used)
+                zig_panic("key not found");
+
+            if (!EqualFn(entry->key, key))
+                continue;
+
+            for (; roll_over < _capacity; roll_over += 1) {
+                int next_index = (start_index + roll_over + 1) % _capacity;
+                Entry *next_entry = &_entries[next_index];
+                if (!next_entry->used || next_entry->distance_from_start_index == 0) {
+                    entry->used = false;
+                    _size -= 1;
+                    return;
+                }
+                *entry = *next_entry;
+                entry->distance_from_start_index -= 1;
+                entry = next_entry;
+            }
+            zig_panic("shifting everything in the table");
+        }
+        zig_panic("key not found");
+    }
+
+    class Iterator {
+    public:
+        Entry *next() {
+            if (_inital_modification_count != _table->_modification_count)
+                zig_panic("concurrent modification");
+            if (_count >= _table->size())
+                return NULL;
+            for (; _index < _table->_capacity; _index += 1) {
+                Entry *entry = &_table->_entries[_index];
+                if (entry->used) {
+                    _index += 1;
+                    _count += 1;
+                    return entry;
+                }
+            }
+            zig_panic("no next item");
+        }
+
+    private:
+        const HashMap * _table;
+        // how many items have we returned
+        int _count = 0;
+        // iterator through the entry array
+        int _index = 0;
+        // used to detect concurrent modification
+        uint32_t _inital_modification_count;
+        Iterator(const HashMap * table) :
+                _table(table), _inital_modification_count(table->_modification_count) {
+        }
+        friend HashMap;
+    };
+
+    // you must not modify the underlying HashMap while this iterator is still in use
+    Iterator entry_iterator() const {
+        return Iterator(this);
+    }
+
+private:
+    Entry *_entries;
+    int _capacity;
+    int _size;
+    int _max_distance_from_start_index;
+    // this is used to detect bugs where a hashtable is edited while an iterator is running.
+    uint32_t _modification_count;
+
+    void init_capacity(Allocator& allocator, int capacity) {
+        _capacity = capacity;
+        _entries = allocator.allocate<Entry>(_capacity);
+        _size = 0;
+        _max_distance_from_start_index = 0;
+        for (int i = 0; i < _capacity; i += 1) {
+            _entries[i].used = false;
+        }
+    }
+
+    void internal_put(K key, V value) {
+        int start_index = key_to_index(key);
+        for (int roll_over = 0, distance_from_start_index = 0;
+                roll_over < _capacity; roll_over += 1, distance_from_start_index += 1)
+        {
+            int index = (start_index + roll_over) % _capacity;
+            Entry *entry = &_entries[index];
+
+            if (entry->used && !EqualFn(entry->key, key)) {
+                if (entry->distance_from_start_index < distance_from_start_index) {
+                    // robin hood to the rescue
+                    Entry tmp = *entry;
+                    if (distance_from_start_index > _max_distance_from_start_index)
+                        _max_distance_from_start_index = distance_from_start_index;
+                    *entry = {
+                        key,
+                        value,
+                        true,
+                        distance_from_start_index,
+                    };
+                    key = tmp.key;
+                    value = tmp.value;
+                    distance_from_start_index = tmp.distance_from_start_index;
+                }
+                continue;
+            }
+
+            if (!entry->used) {
+                // adding an entry. otherwise overwriting old value with
+                // same key
+                _size += 1;
+            }
+
+            if (distance_from_start_index > _max_distance_from_start_index)
+                _max_distance_from_start_index = distance_from_start_index;
+            *entry = {
+                key,
+                value,
+                true,
+                distance_from_start_index,
+            };
+            return;
+        }
+        zig_panic("put into a full HashMap");
+    }
+
+
+    Entry *internal_get(const K &key) const {
+        int start_index = key_to_index(key);
+        for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
+            int index = (start_index + roll_over) % _capacity;
+            Entry *entry = &_entries[index];
+
+            if (!entry->used)
+                return NULL;
+
+            if (EqualFn(entry->key, key))
+                return entry;
+        }
+        return NULL;
+    }
+
+    int key_to_index(const K &key) const {
+        return (int)(HashFunction(key) % ((uint32_t)_capacity));
+    }
+};
+
+} // namespace mem
+
+#endif
src/mem_list.hpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_LIST_HPP
+#define ZIG_MEM_LIST_HPP
+
+#include "mem.hpp"
+
+namespace mem {
+
+template<typename T>
+struct List {
+    void deinit(Allocator& allocator) {
+        allocator.deallocate<T>(items, capacity);
+    }
+
+    void append(Allocator& allocator, const T& item) {
+        ensure_capacity(allocator, length + 1);
+        items[length++] = item;
+    }
+
+    // remember that the pointer to this item is invalid after you
+    // modify the length of the list
+    const T & at(size_t index) const {
+        assert(index != SIZE_MAX);
+        assert(index < length);
+        return items[index];
+    }
+
+    T & at(size_t index) {
+        assert(index != SIZE_MAX);
+        assert(index < length);
+        return items[index];
+    }
+
+    T pop() {
+        assert(length >= 1);
+        return items[--length];
+    }
+
+    T *add_one() {
+        resize(length + 1);
+        return &last();
+    }
+
+    const T & last() const {
+        assert(length >= 1);
+        return items[length - 1];
+    }
+
+    T & last() {
+        assert(length >= 1);
+        return items[length - 1];
+    }
+
+    void resize(Allocator& allocator, size_t new_length) {
+        assert(new_length != SIZE_MAX);
+        ensure_capacity(allocator, new_length);
+        length = new_length;
+    }
+
+    void clear() {
+        length = 0;
+    }
+
+    void ensure_capacity(Allocator& allocator, size_t new_capacity) {
+        if (capacity >= new_capacity)
+            return;
+
+        size_t better_capacity = capacity;
+        do {
+            better_capacity = better_capacity * 5 / 2 + 8;
+        } while (better_capacity < new_capacity);
+
+        items = allocator.reallocate_nonzero<T>(items, capacity, better_capacity);
+        capacity = better_capacity;
+    }
+
+    T swap_remove(size_t index) {
+        if (length - 1 == index) return pop();
+
+        assert(index != SIZE_MAX);
+        assert(index < length);
+
+        T old_item = items[index];
+        items[index] = pop();
+        return old_item;
+    }
+
+    T *items{nullptr};
+    size_t length{0};
+    size_t capacity{0};
+};
+
+} // namespace mem
+
+#endif
src/mem_profile.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "config.h"
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+
+#include "mem.hpp"
+#include "mem_list.hpp"
+#include "mem_profile.hpp"
+#include "heap.hpp"
+
+namespace mem {
+
+void Profile::init(const char *name, const char *kind) {
+    this->name = name;
+    this->kind = kind;
+    this->usage_table.init(heap::bootstrap_allocator, 1024);
+}
+
+void Profile::deinit() {
+    assert(this->name != nullptr);
+    if (mem::report_print)
+        this->print_report();
+    this->usage_table.deinit(heap::bootstrap_allocator);
+    this->name = nullptr;
+}
+
+void Profile::record_alloc(const TypeInfo &info, size_t count) {
+    if (count == 0) return;
+    auto existing_entry = this->usage_table.put_unique(
+        heap::bootstrap_allocator,
+        UsageKey{info.name_ptr, info.name_len},
+        Entry{info, 1, count, 0, 0} );
+    if (existing_entry != nullptr) {
+        assert(existing_entry->value.info.size == info.size); // allocated name does not match type
+        existing_entry->value.alloc.calls += 1;
+        existing_entry->value.alloc.objects += count;
+    }
+}
+
+void Profile::record_dealloc(const TypeInfo &info, size_t count) {
+    if (count == 0) return;
+    auto existing_entry = this->usage_table.maybe_get(UsageKey{info.name_ptr, info.name_len});
+    if (existing_entry == nullptr) {
+        fprintf(stderr, "deallocated name '");
+        for (size_t i = 0; i < info.name_len; ++i)
+            fputc(info.name_ptr[i], stderr);
+        zig_panic("' (size %zu) not found in allocated table; compromised memory usage stats", info.size);
+    }
+    if (existing_entry->value.info.size != info.size) {
+        fprintf(stderr, "deallocated name '");
+        for (size_t i = 0; i < info.name_len; ++i)
+            fputc(info.name_ptr[i], stderr);
+        zig_panic("' does not match expected type size %zu", info.size);
+    }
+    assert(existing_entry->value.alloc.calls - existing_entry->value.dealloc.calls > 0);
+    assert(existing_entry->value.alloc.objects - existing_entry->value.dealloc.objects >= count);
+    existing_entry->value.dealloc.calls += 1;
+    existing_entry->value.dealloc.objects += count;
+}
+
+static size_t entry_remain_total_bytes(const Profile::Entry *entry) {
+    return (entry->alloc.objects - entry->dealloc.objects) * entry->info.size;
+}
+
+static int entry_compare(const void *a, const void *b) {
+    size_t total_a = entry_remain_total_bytes(*reinterpret_cast<Profile::Entry *const *>(a));
+    size_t total_b = entry_remain_total_bytes(*reinterpret_cast<Profile::Entry *const *>(b));
+    if (total_a > total_b)
+        return -1;
+    if (total_a < total_b)
+        return 1;
+    return 0;
+};
+
+void Profile::print_report(FILE *file) {
+    if (!file) {
+        file = report_file;
+        if (!file)
+            file = stderr;
+    }
+    fprintf(file, "\n--- MEMORY PROFILE REPORT [%s]: %s ---\n", this->kind, this->name);
+
+    List<const Entry *> list;
+    auto it = this->usage_table.entry_iterator();
+    for (;;) {
+        auto entry = it.next();
+        if (!entry)
+            break;
+        list.append(heap::bootstrap_allocator, &entry->value);
+    }
+
+    qsort(list.items, list.length, sizeof(const Entry *), entry_compare);
+
+    size_t total_bytes_alloc = 0;
+    size_t total_bytes_dealloc = 0;
+
+    size_t total_calls_alloc = 0;
+    size_t total_calls_dealloc = 0;
+
+    for (size_t i = 0; i < list.length; i += 1) {
+        const Entry *entry = list.at(i);
+        fprintf(file, "  ");
+        for (size_t j = 0; j < entry->info.name_len; ++j)
+            fputc(entry->info.name_ptr[j], file);
+        fprintf(file, ": %zu bytes each", entry->info.size);
+
+        fprintf(file, ", alloc{ %zu calls, %zu objects, total ", entry->alloc.calls, entry->alloc.objects);
+        const auto alloc_num_bytes = entry->alloc.objects * entry->info.size;
+        zig_pretty_print_bytes(file, alloc_num_bytes);
+
+        fprintf(file, " }, dealloc{ %zu calls, %zu objects, total ", entry->dealloc.calls, entry->dealloc.objects);
+        const auto dealloc_num_bytes = entry->dealloc.objects * entry->info.size;
+        zig_pretty_print_bytes(file, dealloc_num_bytes);
+
+        fprintf(file, " }, remain{ %zu calls, %zu objects, total ",
+            entry->alloc.calls - entry->dealloc.calls,
+            entry->alloc.objects - entry->dealloc.objects );
+        const auto remain_num_bytes = alloc_num_bytes - dealloc_num_bytes;
+        zig_pretty_print_bytes(file, remain_num_bytes);
+
+        fprintf(file, " }\n");
+
+        total_bytes_alloc += alloc_num_bytes;
+        total_bytes_dealloc += dealloc_num_bytes;
+
+        total_calls_alloc += entry->alloc.calls;
+        total_calls_dealloc += entry->dealloc.calls;
+    }
+
+    fprintf(file, "\n  Total bytes allocated: ");
+    zig_pretty_print_bytes(file, total_bytes_alloc);
+    fprintf(file, ", deallocated: ");
+    zig_pretty_print_bytes(file, total_bytes_dealloc);
+    fprintf(file, ", remaining: ");
+    zig_pretty_print_bytes(file, total_bytes_alloc - total_bytes_dealloc);
+
+    fprintf(file, "\n  Total calls alloc: %zu, dealloc: %zu, remain: %zu\n",
+        total_calls_alloc, total_calls_dealloc, (total_calls_alloc - total_calls_dealloc));
+
+    list.deinit(heap::bootstrap_allocator);
+}
+
+uint32_t Profile::usage_hash(UsageKey key) {
+    // FNV 32-bit hash
+    uint32_t h = 2166136261;
+    for (size_t i = 0; i < key.name_len; ++i) {
+        h = h ^ key.name_ptr[i];
+        h = h * 16777619;
+    }
+    return h;
+}
+
+bool Profile::usage_equal(UsageKey a, UsageKey b) {
+    return memcmp(a.name_ptr, b.name_ptr, a.name_len > b.name_len ? a.name_len : b.name_len) == 0;
+}
+
+void InternCounters::print_report(FILE *file) {
+    if (!file) {
+        file = report_file;
+        if (!file)
+            file = stderr;
+    }
+    fprintf(file, "\n--- IR INTERNING REPORT ---\n");
+    fprintf(file, "  undefined: interned %zu times\n", intern_counters.x_undefined);
+    fprintf(file, "  void: interned %zu times\n", intern_counters.x_void);
+    fprintf(file, "  null: interned %zu times\n", intern_counters.x_null);
+    fprintf(file, "  unreachable: interned %zu times\n", intern_counters.x_unreachable);
+    fprintf(file, "  zero_byte: interned %zu times\n", intern_counters.zero_byte);
+}
+
+InternCounters intern_counters;
+
+} // namespace mem
+
+#endif
src/mem_profile.hpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_PROFILE_HPP
+#define ZIG_MEM_PROFILE_HPP
+
+#include "config.h"
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+
+#include <stdio.h>
+
+#include "mem.hpp"
+#include "mem_hash_map.hpp"
+#include "util.hpp"
+
+namespace mem {
+
+struct Profile {
+    void init(const char *name, const char *kind);
+    void deinit();
+
+    void record_alloc(const TypeInfo &info, size_t count);
+    void record_dealloc(const TypeInfo &info, size_t count);
+
+    void print_report(FILE *file = nullptr);
+
+    struct Entry {
+        TypeInfo info;
+
+        struct Use {
+            size_t calls;
+            size_t objects;
+        } alloc, dealloc;
+    };
+
+private:
+    const char *name;
+    const char *kind;
+
+    struct UsageKey {
+        const char *name_ptr;
+        size_t name_len;
+    };
+
+    static uint32_t usage_hash(UsageKey key);
+    static bool usage_equal(UsageKey a, UsageKey b);
+
+    HashMap<UsageKey, Entry, usage_hash, usage_equal> usage_table;
+};
+
+struct InternCounters {
+    size_t x_undefined;
+    size_t x_void;
+    size_t x_null;
+    size_t x_unreachable;
+    size_t zero_byte;
+
+    void print_report(FILE *file = nullptr);
+};
+
+extern InternCounters intern_counters;
+
+} // namespace mem
+
+#endif
+#endif
src/mem_type_info.hpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_TYPE_INFO_HPP
+#define ZIG_MEM_TYPE_INFO_HPP
+
+#include "config.h"
+
+#ifndef ZIG_TYPE_INFO_IMPLEMENTATION
+#   ifdef ZIG_ENABLE_MEM_PROFILE
+#       define ZIG_TYPE_INFO_IMPLEMENTATION 1
+#   else
+#       define ZIG_TYPE_INFO_IMPLEMENTATION 0
+#   endif
+#endif
+
+namespace mem {
+
+#if ZIG_TYPE_INFO_IMPLEMENTATION == 0
+
+struct TypeInfo {
+    size_t size;
+    size_t alignment;
+
+    template <typename T>
+    static constexpr TypeInfo make() {
+        return {sizeof(T), alignof(T)};
+    }
+};
+
+#elif ZIG_TYPE_INFO_IMPLEMENTATION == 1
+
+//
+// A non-portable way to get a human-readable type-name compatible with
+// non-RTTI C++ compiler mode; eg. `-fno-rtti`.
+//
+// Minimum requirements are c++11 and a compiler that has a constant for the
+// current function's decorated name whereby a template-type name can be
+// computed. eg. `__PRETTY_FUNCTION__` or `__FUNCSIG__`.
+//
+// given the following snippet:
+//
+//     |  #include <stdio.h>
+//     |
+//     |  struct Top {};
+//     |  namespace mynamespace {
+//     |      using custom = unsigned int;
+//     |      struct Foo {
+//     |          struct Bar {};
+//     |      };
+//     |  };
+//     |
+//     |  template <typename T>
+//     |  void foobar() {
+//     |  #ifdef _MSC_VER
+//     |      fprintf(stderr, "--> %s\n", __FUNCSIG__);
+//     |  #else
+//     |      fprintf(stderr, "--> %s\n", __PRETTY_FUNCTION__);
+//     |  #endif
+//     |  }
+//     |
+//     |  int main() {
+//     |      foobar<Top>();
+//     |      foobar<unsigned int>();
+//     |      foobar<mynamespace::custom>();
+//     |      foobar<mynamespace::Foo*>();
+//     |      foobar<mynamespace::Foo::Bar*>();
+//     |  }
+//
+// gcc 9.2.0 produces:
+//   --> void foobar() [with T = Top]
+//   --> void foobar() [with T = unsigned int]
+//   --> void foobar() [with T = unsigned int]
+//   --> void foobar() [with T = mynamespace::Foo*]
+//   --> void foobar() [with T = mynamespace::Foo::Bar*]
+//
+// xcode 11.3.1/clang produces:
+//   --> void foobar() [T = Top]
+//   --> void foobar() [T = unsigned int]
+//   --> void foobar() [T = unsigned int]
+//   --> void foobar() [T = mynamespace::Foo *]
+//   --> void foobar() [T = mynamespace::Foo::Bar *]
+//
+// VStudio 2019 16.5.0/msvc produces:
+//   --> void __cdecl foobar<struct Top>(void)
+//   --> void __cdecl foobar<unsigned int>(void)
+//   --> void __cdecl foobar<unsigned int>(void)
+//   --> void __cdecl foobar<structmynamespace::Foo*>(void)
+//   --> void __cdecl foobar<structmynamespace::Foo::Bar*>(void)
+//
+struct TypeInfo {
+    const char *name_ptr;
+    size_t name_len;
+    size_t size;
+    size_t alignment;
+
+    static constexpr TypeInfo to_type_info(const char *str, size_t start, size_t end, size_t size, size_t alignment) {
+        return TypeInfo{str + start, end - start, size, alignment};
+    }
+
+    static constexpr size_t index_of(const char *str, char c) {
+        return *str == c ? 0 : 1 + index_of(str + 1, c);
+    }
+
+    template <typename T>
+    static constexpr const char *decorated_name() {
+#ifdef _MSC_VER
+        return __FUNCSIG__;
+#else
+        return __PRETTY_FUNCTION__;
+#endif
+    }
+
+    static constexpr TypeInfo extract(const char *decorated, size_t size, size_t alignment) {
+#ifdef _MSC_VER
+        return to_type_info(decorated, index_of(decorated, '<') + 1, index_of(decorated, '>'), size, alignment);
+#else
+        return to_type_info(decorated, index_of(decorated, '=') + 2, index_of(decorated, ']'), size, alignment);
+#endif
+    }
+
+    template <typename T>
+    static constexpr TypeInfo make() {
+        return TypeInfo::extract(TypeInfo::decorated_name<T>(), sizeof(T), alignof(T));
+    }
+};
+
+#endif // ZIG_TYPE_INFO_IMPLEMENTATION
+
+} // namespace mem
+
+#endif
src/memory_profiling.cpp
@@ -1,150 +0,0 @@
-#include "memory_profiling.hpp"
-#include "hash_map.hpp"
-#include "list.hpp"
-#include "util.hpp"
-#include <string.h>
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-
-MemprofInternCount memprof_intern_count;
-
-static bool str_eql_str(const char *a, const char *b) {
-    return strcmp(a, b) == 0;
-}
-
-static uint32_t str_hash(const char *s) {
-    // FNV 32-bit hash
-    uint32_t h = 2166136261;
-    for (; *s; s += 1) {
-        h = h ^ *s;
-        h = h * 16777619;
-    }
-    return h;
-}
-
-struct CountAndSize {
-    size_t item_count;
-    size_t type_size;
-};
-
-ZigList<const char *> unknown_names = {};
-HashMap<const char *, CountAndSize, str_hash, str_eql_str> usage_table = {};
-bool table_active = false;
-
-static const char *get_default_name(const char *name_or_null, size_t type_size) {
-    if (name_or_null != nullptr) return name_or_null;
-    if (type_size >= unknown_names.length) {
-        table_active = false;
-        while (type_size >= unknown_names.length) {
-            unknown_names.append(nullptr);
-        }
-        table_active = true;
-    }
-    if (unknown_names.at(type_size) == nullptr) {
-        char buf[100];
-        sprintf(buf, "Unknown_%zu%c", type_size, 0);
-        unknown_names.at(type_size) = strdup(buf);
-    }
-    return unknown_names.at(type_size);
-}
-
-void memprof_alloc(const char *name, size_t count, size_t type_size) {
-    if (!table_active) return;
-    if (count == 0) return;
-    // temporarily disable during table put
-    table_active = false;
-    name = get_default_name(name, type_size);
-    auto existing_entry = usage_table.put_unique(name, {count, type_size});
-    if (existing_entry != nullptr) {
-        assert(existing_entry->value.type_size == type_size); // allocated name does not match type
-        existing_entry->value.item_count += count;
-    }
-    table_active = true;
-}
-
-void memprof_dealloc(const char *name, size_t count, size_t type_size) {
-    if (!table_active) return;
-    if (count == 0) return;
-    name = get_default_name(name, type_size);
-    auto existing_entry = usage_table.maybe_get(name);
-    if (existing_entry == nullptr) {
-        zig_panic("deallocated name '%s' (size %zu) not found in allocated table; compromised memory usage stats",
-                name, type_size);
-    }
-    if (existing_entry->value.type_size != type_size) {
-        zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size);
-    }
-    existing_entry->value.item_count -= count;
-}
-
-void memprof_init(void) {
-    usage_table.init(1024);
-    table_active = true;
-}
-
-struct MemItem {
-    const char *type_name;
-    CountAndSize count_and_size;
-};
-
-static size_t get_bytes(const MemItem *item) {
-    return item->count_and_size.item_count * item->count_and_size.type_size;
-}
-
-static int compare_bytes_desc(const void *a, const void *b) {
-    size_t size_a = get_bytes((const MemItem *)(a));
-    size_t size_b = get_bytes((const MemItem *)(b));
-    if (size_a > size_b)
-        return -1;
-    if (size_a < size_b)
-        return 1;
-    return 0;
-}
-
-void memprof_dump_stats(FILE *file) {
-    assert(table_active);
-    // disable modifications from this function
-    table_active = false;
-
-    ZigList<MemItem> list = {};
-
-    auto it = usage_table.entry_iterator();
-    for (;;) {
-        auto *entry = it.next();
-        if (!entry)
-            break;
-
-        list.append({entry->key, entry->value});
-    }
-
-    qsort(list.items, list.length, sizeof(MemItem), compare_bytes_desc);
-
-    size_t total_bytes_used = 0;
-
-    for (size_t i = 0; i < list.length; i += 1) {
-        const MemItem *item = &list.at(i);
-        fprintf(file, "%s: %zu items, %zu bytes each, total ", item->type_name,
-                item->count_and_size.item_count, item->count_and_size.type_size);
-        size_t bytes = get_bytes(item);
-        zig_pretty_print_bytes(file, bytes);
-        fprintf(file, "\n");
-
-        total_bytes_used += bytes;
-    }
-
-    fprintf(stderr, "Total bytes used: ");
-    zig_pretty_print_bytes(file, total_bytes_used);
-    fprintf(file, "\n");
-
-    list.deinit();
-    table_active = true;
-
-    fprintf(stderr, "\n");
-    fprintf(stderr, "undefined: interned %zu times\n", memprof_intern_count.x_undefined);
-    fprintf(stderr, "void: interned %zu times\n", memprof_intern_count.x_void);
-    fprintf(stderr, "null: interned %zu times\n", memprof_intern_count.x_null);
-    fprintf(stderr, "unreachable: interned %zu times\n", memprof_intern_count.x_unreachable);
-    fprintf(stderr, "zero_byte: interned %zu times\n", memprof_intern_count.zero_byte);
-}
-
-#endif
src/memory_profiling.hpp
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_MEMORY_PROFILING_HPP
-#define ZIG_MEMORY_PROFILING_HPP
-
-#include "config.h"
-
-#include <stddef.h>
-#include <stdio.h>
-
-struct MemprofInternCount {
-    size_t x_undefined;
-    size_t x_void;
-    size_t x_null;
-    size_t x_unreachable;
-    size_t zero_byte;
-};
-extern MemprofInternCount memprof_intern_count;
-
-void memprof_init(void);
-
-void memprof_alloc(const char *name, size_t item_count, size_t type_size);
-void memprof_dealloc(const char *name, size_t item_count, size_t type_size);
-
-void memprof_dump_stats(FILE *file);
-#endif
src/os.cpp
@@ -107,7 +107,7 @@ static void populate_termination(Termination *term, int status) {
 }
 
 static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) {
-    const char **argv = allocate<const char *>(args.length + 1);
+    const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1);
     for (size_t i = 0; i < args.length; i += 1) {
         argv[i] = args.at(i);
     }
@@ -688,7 +688,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) {
 
     if (have_abs) {
         result_len = max_size;
-        result_ptr = allocate_nonzero<uint8_t>(result_len);
+        result_ptr = heap::c_allocator.allocate_nonzero<uint8_t>(result_len);
     } else {
         Buf cwd = BUF_INIT;
         int err;
@@ -696,7 +696,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) {
             zig_panic("get cwd failed");
         }
         result_len = max_size + buf_len(&cwd) + 1;
-        result_ptr = allocate_nonzero<uint8_t>(result_len);
+        result_ptr = heap::c_allocator.allocate_nonzero<uint8_t>(result_len);
         memcpy(result_ptr, buf_ptr(&cwd), buf_len(&cwd));
         result_index += buf_len(&cwd);
     }
@@ -816,7 +816,7 @@ static Error os_exec_process_posix(ZigList<const char *> &args,
         if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
             zig_panic("dup2 failed");
 
-        const char **argv = allocate<const char *>(args.length + 1);
+        const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1);
         argv[args.length] = nullptr;
         for (size_t i = 0; i < args.length; i += 1) {
             argv[i] = args.at(i);
@@ -1134,7 +1134,7 @@ static bool is_stderr_cyg_pty(void) {
     if (stderr_handle == INVALID_HANDLE_VALUE)
         return false;
 
-    int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
+    const int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
     FILE_NAME_INFO *nameinfo;
     WCHAR *p = NULL;
 
@@ -1142,7 +1142,7 @@ static bool is_stderr_cyg_pty(void) {
     if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) {
         return 0;
     }
-    nameinfo = (FILE_NAME_INFO *)allocate<char>(size);
+    nameinfo = reinterpret_cast<FILE_NAME_INFO *>(heap::c_allocator.allocate<char>(size));
     if (nameinfo == NULL) {
         return 0;
     }
@@ -1179,7 +1179,7 @@ static bool is_stderr_cyg_pty(void) {
             }
         }
     }
-    free(nameinfo);
+    heap::c_allocator.deallocate(reinterpret_cast<char *>(nameinfo), size);
     return (p != NULL);
 }
 #endif
src/parser.cpp
@@ -147,7 +147,7 @@ static void ast_invalid_token_error(ParseContext *pc, Token *token) {
 }
 
 static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
-    AstNode *node = allocate<AstNode>(1, "AstNode");
+    AstNode *node = heap::c_allocator.create<AstNode>();
     node->type = type;
     node->owner = pc->owner;
     return node;
@@ -1966,7 +1966,7 @@ static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) {
 
     expect_token(pc, TokenIdRParen);
 
-    AsmOutput *res = allocate<AsmOutput>(1);
+    AsmOutput *res = heap::c_allocator.create<AsmOutput>();
     res->asm_symbolic_name = token_buf(sym_name);
     res->constraint = token_buf(str);
     res->variable_name = token_buf(var_name);
@@ -2003,7 +2003,7 @@ static AsmInput *ast_parse_asm_input_item(ParseContext *pc) {
     AstNode *expr = ast_expect(pc, ast_parse_expr);
     expect_token(pc, TokenIdRParen);
 
-    AsmInput *res = allocate<AsmInput>(1);
+    AsmInput *res = heap::c_allocator.create<AsmInput>();
     res->asm_symbolic_name = token_buf(sym_name);
     res->constraint = token_buf(constraint);
     res->expr = expr;
src/target.cpp
@@ -520,7 +520,7 @@ void get_native_target(ZigTarget *target) {
         target->abi = target_default_abi(target->arch, target->os);
     }
     if (target_is_glibc(target)) {
-        target->glibc_version = allocate<ZigGLibCVersion>(1);
+        target->glibc_version = heap::c_allocator.create<ZigGLibCVersion>();
         target_init_default_glibc_version(target);
 #ifdef ZIG_OS_LINUX
         Error err;
src/tokenizer.cpp
@@ -397,10 +397,10 @@ static void invalid_char_error(Tokenize *t, uint8_t c) {
 void tokenize(Buf *buf, Tokenization *out) {
     Tokenize t = {0};
     t.out = out;
-    t.tokens = out->tokens = allocate<ZigList<Token>>(1);
+    t.tokens = out->tokens = heap::c_allocator.create<ZigList<Token>>();
     t.buf = buf;
 
-    out->line_offsets = allocate<ZigList<size_t>>(1);
+    out->line_offsets = heap::c_allocator.create<ZigList<size_t>>();
     out->line_offsets->append(0);
 
     // Skip the UTF-8 BOM if present
src/userland.cpp
@@ -101,7 +101,7 @@ Error stage2_cpu_features_parse(struct Stage2CpuFeatures **out, const char *zig_
         const char *cpu_name, const char *cpu_features)
 {
     if (zig_triple == nullptr) {
-        Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");
+        Stage2CpuFeatures *result = heap::c_allocator.create<Stage2CpuFeatures>();
         result->llvm_cpu_name = ZigLLVMGetHostCPUName();
         result->llvm_cpu_features = ZigLLVMGetNativeFeatures();
         result->builtin_str = "arch.getBaselineCpuFeatures();\n";
@@ -110,7 +110,7 @@ Error stage2_cpu_features_parse(struct Stage2CpuFeatures **out, const char *zig_
         return ErrorNone;
     }
     if (cpu_name == nullptr && cpu_features == nullptr) {
-        Stage2CpuFeatures *result = allocate<Stage2CpuFeatures>(1, "Stage2CpuFeatures");
+        Stage2CpuFeatures *result = heap::c_allocator.create<Stage2CpuFeatures>();
         result->builtin_str = "arch.getBaselineCpuFeatures();\n";
         result->cache_hash = "\n\n";
         *out = result;
src/util.hpp
@@ -8,69 +8,19 @@
 #ifndef ZIG_UTIL_HPP
 #define ZIG_UTIL_HPP
 
-#include "memory_profiling.hpp"
-
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
-#include <assert.h>
 #include <ctype.h>
 
 #if defined(_MSC_VER)
-
 #include <intrin.h>  
-
-#define ATTRIBUTE_COLD __declspec(noinline)
-#define ATTRIBUTE_PRINTF(a, b)
-#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
-#define ATTRIBUTE_NORETURN __declspec(noreturn)
-#define ATTRIBUTE_MUST_USE
-
-#define BREAKPOINT __debugbreak()
-
-#else
-
-#define ATTRIBUTE_COLD         __attribute__((cold))
-#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b)))
-#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
-#define ATTRIBUTE_NORETURN __attribute__((noreturn))
-#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result))
-
-#if defined(__MINGW32__) || defined(__MINGW64__)
-#define BREAKPOINT __debugbreak()
-#elif defined(__i386__) || defined(__x86_64__)
-#define BREAKPOINT __asm__ volatile("int $0x03");
-#elif defined(__clang__)
-#define BREAKPOINT __builtin_debugtrap()
-#elif defined(__GNUC__)
-#define BREAKPOINT __builtin_trap()
-#else
-#include <signal.h>
-#define BREAKPOINT raise(SIGTRAP)
-#endif
-
-#endif
-
-ATTRIBUTE_COLD
-ATTRIBUTE_NORETURN
-ATTRIBUTE_PRINTF(1, 2)
-void zig_panic(const char *format, ...);
-
-static inline void zig_assert(bool ok, const char *file, int line, const char *func) {
-    if (!ok) {
-        zig_panic("Assertion failed at %s:%d in %s. This is a bug in the Zig compiler.", file, line, func);
-    }
-}
-
-#ifdef _WIN32
-#define __func__ __FUNCTION__
 #endif
 
-#define zig_unreachable() zig_panic("Unreachable at %s:%d in %s. This is a bug in the Zig compiler.", __FILE__, __LINE__, __func__)
-
-// Assertions in stage1 are always on, and they call zig @panic.
-#undef assert
-#define assert(ok) zig_assert(ok, __FILE__, __LINE__, __func__)
+#include "config.h"
+#include "util_base.hpp"
+#include "heap.hpp"
+#include "mem.hpp"
 
 #if defined(_MSC_VER)
 static inline int clzll(unsigned long long mask) {
@@ -107,78 +57,6 @@ static inline int ctzll(unsigned long long mask) {
 #define ctzll(x) __builtin_ctzll(x)
 #endif
 
-
-template<typename T>
-ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count, const char *name = nullptr) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_alloc(name, count, sizeof(T));
-#endif
-#ifndef NDEBUG
-    // make behavior when size == 0 portable
-    if (count == 0)
-        return nullptr;
-#endif
-    T *ptr = reinterpret_cast<T*>(malloc(count * sizeof(T)));
-    if (!ptr)
-        zig_panic("allocation failed");
-    return ptr;
-}
-
-template<typename T>
-ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count, const char *name = nullptr) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_alloc(name, count, sizeof(T));
-#endif
-#ifndef NDEBUG
-    // make behavior when size == 0 portable
-    if (count == 0)
-        return nullptr;
-#endif
-    T *ptr = reinterpret_cast<T*>(calloc(count, sizeof(T)));
-    if (!ptr)
-        zig_panic("allocation failed");
-    return ptr;
-}
-
-template<typename T>
-static inline T *reallocate(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
-    T *ptr = reallocate_nonzero(old, old_count, new_count);
-    if (new_count > old_count) {
-        memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
-    }
-    return ptr;
-}
-
-template<typename T>
-static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_dealloc(name, old_count, sizeof(T));
-    memprof_alloc(name, new_count, sizeof(T));
-#endif
-#ifndef NDEBUG
-    // make behavior when size == 0 portable
-    if (new_count == 0 && old == nullptr)
-        return nullptr;
-#endif
-    T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
-    if (!ptr)
-        zig_panic("allocation failed");
-    return ptr;
-}
-
-template<typename T>
-static inline void deallocate(T *old, size_t count, const char *name = nullptr) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
-    memprof_dealloc(name, count, sizeof(T));
-#endif
-    free(old);
-}
-
-template<typename T>
-static inline void destroy(T *old, const char *name = nullptr) {
-    return deallocate(old, 1, name);
-}
-
 template <typename T, size_t n>
 constexpr size_t array_length(const T (&)[n]) {
     return n;
@@ -293,7 +171,7 @@ struct Slice {
     }
 
     static inline Slice<T> alloc(size_t n) {
-        return {allocate_nonzero<T>(n), n};
+        return {heap::c_allocator.allocate_nonzero<T>(n), n};
     }
 };
 
src/util_base.hpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_UTIL_BASE_HPP
+#define ZIG_UTIL_BASE_HPP
+
+#include <assert.h>
+
+#if defined(_MSC_VER)
+
+#define ATTRIBUTE_COLD __declspec(noinline)
+#define ATTRIBUTE_PRINTF(a, b)
+#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
+#define ATTRIBUTE_NORETURN __declspec(noreturn)
+#define ATTRIBUTE_MUST_USE
+
+#define BREAKPOINT __debugbreak()
+
+#else
+
+#define ATTRIBUTE_COLD         __attribute__((cold))
+#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b)))
+#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
+#define ATTRIBUTE_NORETURN __attribute__((noreturn))
+#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result))
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#define BREAKPOINT __debugbreak()
+#elif defined(__i386__) || defined(__x86_64__)
+#define BREAKPOINT __asm__ volatile("int $0x03");
+#elif defined(__clang__)
+#define BREAKPOINT __builtin_debugtrap()
+#elif defined(__GNUC__)
+#define BREAKPOINT __builtin_trap()
+#else
+#include <signal.h>
+#define BREAKPOINT raise(SIGTRAP)
+#endif
+
+#endif
+
+ATTRIBUTE_COLD
+ATTRIBUTE_NORETURN
+ATTRIBUTE_PRINTF(1, 2)
+void zig_panic(const char *format, ...);
+
+static inline void zig_assert(bool ok, const char *file, int line, const char *func) {
+    if (!ok) {
+        zig_panic("Assertion failed at %s:%d in %s. This is a bug in the Zig compiler.", file, line, func);
+    }
+}
+
+#ifdef _WIN32
+#define __func__ __FUNCTION__
+#endif
+
+#define zig_unreachable() zig_panic("Unreachable at %s:%d in %s. This is a bug in the Zig compiler.", __FILE__, __LINE__, __func__)
+
+// Assertions in stage1 are always on, and they call zig @panic.
+#undef assert
+#define assert(ok) zig_assert(ok, __FILE__, __LINE__, __func__)
+
+#endif
CMakeLists.txt
@@ -450,7 +450,7 @@ set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp")
 set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp")
 
 if(ZIG_ENABLE_MEM_PROFILE)
-    set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp")
+    set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/mem_profile.cpp")
 endif()
 
 set(ZIG_SOURCES
@@ -466,10 +466,12 @@ set(ZIG_SOURCES
     "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
     "${CMAKE_SOURCE_DIR}/src/error.cpp"
     "${CMAKE_SOURCE_DIR}/src/glibc.cpp"
+    "${CMAKE_SOURCE_DIR}/src/heap.cpp"
     "${CMAKE_SOURCE_DIR}/src/ir.cpp"
     "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
     "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
     "${CMAKE_SOURCE_DIR}/src/link.cpp"
+    "${CMAKE_SOURCE_DIR}/src/mem.cpp"
     "${CMAKE_SOURCE_DIR}/src/os.cpp"
     "${CMAKE_SOURCE_DIR}/src/parser.cpp"
     "${CMAKE_SOURCE_DIR}/src/range_set.cpp"