Commit f8fe517d12

Andrew Kelley <superjoe30@gmail.com>
2018-09-20 17:04:31
better string literal caching implementation
We were caching the ConstExprValue of string literals, which works if you can never modify ConstExprValues. This premise is broken with `comptime var ...`. So I implemented an optimization in ConstExprValue arrays, where it stores a `Buf *` directly rather than an array of ConstExprValues for the elements, and then similar to array of undefined, it is expanded into the canonical form when necessary. However many operations can happen directly on the `Buf *`, which is faster. Furthermore, before a ConstExprValue array is expanded into canonical form, it removes itself from the string literal cache. This fixes the issue, because before an array element is modified it would have to be expanded. closes #1076
1 parent 4928217
src/all_types.hpp
@@ -130,14 +130,18 @@ struct ConstUnionValue {
 enum ConstArraySpecial {
     ConstArraySpecialNone,
     ConstArraySpecialUndef,
+    ConstArraySpecialBuf,
 };
 
 struct ConstArrayValue {
     ConstArraySpecial special;
-    struct {
-        ConstExprValue *elements;
-        ConstParent parent;
-    } s_none;
+    union {
+        struct {
+            ConstExprValue *elements;
+            ConstParent parent;
+        } s_none;
+        Buf *s_buf;
+    } data;
 };
 
 enum ConstPtrSpecial {
@@ -983,6 +987,7 @@ struct FnTypeParamInfo {
 };
 
 struct GenericFnTypeId {
+    CodeGen *codegen;
     ZigFn *fn_entry;
     ConstExprValue *params;
     size_t param_count;
@@ -1291,6 +1296,7 @@ struct FnExport {
 };
 
 struct ZigFn {
+    CodeGen *codegen;
     LLVMValueRef llvm_value;
     const char *llvm_name;
     AstNode *proto_node;
@@ -1848,13 +1854,14 @@ enum ScopeId {
 };
 
 struct Scope {
-    ScopeId id;
+    CodeGen *codegen;
     AstNode *source_node;
 
     // if the scope has a parent, this is it
     Scope *parent;
 
     ZigLLVMDIScope *di_scope;
+    ScopeId id;
 };
 
 // This scope comes from global declarations or from
src/analyze.cpp
@@ -92,62 +92,63 @@ ScopeDecls *get_container_scope(ZigType *type_entry) {
     return *get_container_scope_ptr(type_entry);
 }
 
-void init_scope(Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
+void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
+    dest->codegen = g;
     dest->id = id;
     dest->source_node = source_node;
     dest->parent = parent;
 }
 
-ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import) {
+ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import) {
     assert(node == nullptr || node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr);
     ScopeDecls *scope = allocate<ScopeDecls>(1);
-    init_scope(&scope->base, ScopeIdDecls, node, parent);
+    init_scope(g, &scope->base, ScopeIdDecls, node, parent);
     scope->decl_table.init(4);
     scope->container_type = container_type;
     scope->import = import;
     return scope;
 }
 
-ScopeBlock *create_block_scope(AstNode *node, Scope *parent) {
+ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeBlock);
     ScopeBlock *scope = allocate<ScopeBlock>(1);
-    init_scope(&scope->base, ScopeIdBlock, node, parent);
+    init_scope(g, &scope->base, ScopeIdBlock, node, parent);
     scope->name = node->data.block.name;
     return scope;
 }
 
-ScopeDefer *create_defer_scope(AstNode *node, Scope *parent) {
+ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeDefer);
     ScopeDefer *scope = allocate<ScopeDefer>(1);
-    init_scope(&scope->base, ScopeIdDefer, node, parent);
+    init_scope(g, &scope->base, ScopeIdDefer, node, parent);
     return scope;
 }
 
-ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent) {
+ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeDefer);
     ScopeDeferExpr *scope = allocate<ScopeDeferExpr>(1);
-    init_scope(&scope->base, ScopeIdDeferExpr, node, parent);
+    init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
     return scope;
 }
 
-Scope *create_var_scope(AstNode *node, Scope *parent, ZigVar *var) {
+Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
     ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
-    init_scope(&scope->base, ScopeIdVarDecl, node, parent);
+    init_scope(g, &scope->base, ScopeIdVarDecl, node, parent);
     scope->var = var;
     return &scope->base;
 }
 
-ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
+ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeFnCallExpr);
     ScopeCImport *scope = allocate<ScopeCImport>(1);
-    init_scope(&scope->base, ScopeIdCImport, node, parent);
+    init_scope(g, &scope->base, ScopeIdCImport, node, parent);
     buf_resize(&scope->buf, 0);
     return scope;
 }
 
-ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
+ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
     ScopeLoop *scope = allocate<ScopeLoop>(1);
-    init_scope(&scope->base, ScopeIdLoop, node, parent);
+    init_scope(g, &scope->base, ScopeIdLoop, node, parent);
     if (node->type == NodeTypeWhileExpr) {
         scope->name = node->data.while_expr.name;
     } else if (node->type == NodeTypeForExpr) {
@@ -158,37 +159,37 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
     return scope;
 }
 
-Scope *create_runtime_scope(AstNode *node, Scope *parent, IrInstruction *is_comptime) {
+Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime) {
     ScopeRuntime *scope = allocate<ScopeRuntime>(1);
     scope->is_comptime = is_comptime;
-    init_scope(&scope->base, ScopeIdRuntime, node, parent);
+    init_scope(g, &scope->base, ScopeIdRuntime, node, parent);
     return &scope->base;
 }
 
-ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
+ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeSuspend);
     ScopeSuspend *scope = allocate<ScopeSuspend>(1);
-    init_scope(&scope->base, ScopeIdSuspend, node, parent);
+    init_scope(g, &scope->base, ScopeIdSuspend, node, parent);
     return scope;
 }
 
-ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, ZigFn *fn_entry) {
+ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) {
     ScopeFnDef *scope = allocate<ScopeFnDef>(1);
-    init_scope(&scope->base, ScopeIdFnDef, node, parent);
+    init_scope(g, &scope->base, ScopeIdFnDef, node, parent);
     scope->fn_entry = fn_entry;
     return scope;
 }
 
-Scope *create_comptime_scope(AstNode *node, Scope *parent) {
+Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeCompTime || node->type == NodeTypeSwitchExpr);
     ScopeCompTime *scope = allocate<ScopeCompTime>(1);
-    init_scope(&scope->base, ScopeIdCompTime, node, parent);
+    init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
     return &scope->base;
 }
 
-Scope *create_coro_prelude_scope(AstNode *node, Scope *parent) {
+Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent) {
     ScopeCoroPrelude *scope = allocate<ScopeCoroPrelude>(1);
-    init_scope(&scope->base, ScopeIdCoroPrelude, node, parent);
+    init_scope(g, &scope->base, ScopeIdCoroPrelude, node, parent);
     return &scope->base;
 }
 
@@ -204,9 +205,9 @@ ImportTableEntry *get_scope_import(Scope *scope) {
     zig_unreachable();
 }
 
-static ZigType *new_container_type_entry(ZigTypeId id, AstNode *source_node, Scope *parent_scope) {
+static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope) {
     ZigType *entry = new_type_table_entry(id);
-    *get_container_scope_ptr(entry) = create_decls_scope(source_node, parent_scope, entry, get_scope_import(parent_scope));
+    *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry, get_scope_import(parent_scope));
     return entry;
 }
 
@@ -1245,7 +1246,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind
         AstNode *decl_node, const char *name, ContainerLayout layout)
 {
     ZigTypeId type_id = container_to_type(kind);
-    ZigType *entry = new_container_type_entry(type_id, decl_node, scope);
+    ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope);
 
     switch (kind) {
         case ContainerKindStruct:
@@ -1372,13 +1373,17 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **
 
     assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
     ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
+    if (array_val->data.x_array.special == ConstArraySpecialBuf) {
+        *out_buffer = array_val->data.x_array.data.s_buf;
+        return true;
+    }
     expand_undef_array(g, array_val);
     size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
     Buf *result = buf_alloc();
     buf_resize(result, len);
     for (size_t i = 0; i < len; i += 1) {
         size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
-        ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index];
+        ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
         if (char_val->special == ConstValSpecialUndef) {
             add_node_error(g, node, buf_sprintf("use of undefined value"));
             return false;
@@ -3093,9 +3098,10 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) {
     buf_append_buf(buf, tld->name);
 }
 
-ZigFn *create_fn_raw(FnInline inline_value) {
+ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
     ZigFn *fn_entry = allocate<ZigFn>(1);
 
+    fn_entry->codegen = g;
     fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
     fn_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota;
     fn_entry->analyzed_executable.fn_entry = fn_entry;
@@ -3105,12 +3111,12 @@ ZigFn *create_fn_raw(FnInline inline_value) {
     return fn_entry;
 }
 
-ZigFn *create_fn(AstNode *proto_node) {
+ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
     assert(proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
     FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
-    ZigFn *fn_entry = create_fn_raw(inline_value);
+    ZigFn *fn_entry = create_fn_raw(g, inline_value);
 
     fn_entry->proto_node = proto_node;
     fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
@@ -3209,7 +3215,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
 
         AstNode *fn_def_node = fn_proto->fn_def_node;
 
-        ZigFn *fn_table_entry = create_fn(source_node);
+        ZigFn *fn_table_entry = create_fn(g, source_node);
         get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
 
         if (fn_proto->is_export) {
@@ -3220,7 +3226,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
         tld_fn->fn_entry = fn_table_entry;
 
         if (fn_table_entry->body_node) {
-            fn_table_entry->fndef_scope = create_fndef_scope(
+            fn_table_entry->fndef_scope = create_fndef_scope(g,
                 fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry);
 
             for (size_t i = 0; i < fn_proto->params.length; i += 1) {
@@ -3270,14 +3276,14 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
             }
         }
     } else if (source_node->type == NodeTypeTestDecl) {
-        ZigFn *fn_table_entry = create_fn_raw(FnInlineAuto);
+        ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
 
         get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
 
         tld_fn->fn_entry = fn_table_entry;
 
         fn_table_entry->proto_node = source_node;
-        fn_table_entry->fndef_scope = create_fndef_scope(source_node, tld_fn->base.parent_scope, fn_table_entry);
+        fn_table_entry->fndef_scope = create_fndef_scope(g, source_node, tld_fn->base.parent_scope, fn_table_entry);
         fn_table_entry->type_entry = get_test_fn_type(g);
         fn_table_entry->body_node = source_node->data.test_decl.body;
         fn_table_entry->is_test = true;
@@ -3606,7 +3612,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
 
     Scope *child_scope;
     if (source_node && source_node->type == NodeTypeParamDecl) {
-        child_scope = create_var_scope(source_node, parent_scope, variable_entry);
+        child_scope = create_var_scope(g, source_node, parent_scope, variable_entry);
     } else {
         // it's already in the decls table
         child_scope = parent_scope;
@@ -4329,7 +4335,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *r
     g->import_table.put(resolved_path, import_entry);
     g->import_queue.append(import_entry);
 
-    import_entry->decls_scope = create_decls_scope(import_entry->root, nullptr, nullptr, import_entry);
+    import_entry->decls_scope = create_decls_scope(g, import_entry->root, nullptr, nullptr, import_entry);
 
 
     assert(import_entry->root->type == NodeTypeRoot);
@@ -4880,7 +4886,7 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
         if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
             assert(a_val->special == ConstValSpecialStatic);
             assert(b_val->special == ConstValSpecialStatic);
-            if (!const_values_equal(a_val, b_val)) {
+            if (!const_values_equal(a->fn_entry->codegen, a_val, b_val)) {
                 return false;
             }
         } else {
@@ -4920,14 +4926,18 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
         case ZigTypeIdArray:
             if (value->type->data.array.len == 0)
                 return false;
-            if (value->data.x_array.special == ConstArraySpecialUndef)
-                return false;
-            for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
-                if (can_mutate_comptime_var_state(&value->data.x_array.s_none.elements[i]))
-                    return true;
+            switch (value->data.x_array.special) {
+                case ConstArraySpecialUndef:
+                case ConstArraySpecialBuf:
+                    return false;
+                case ConstArraySpecialNone:
+                    for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
+                        if (can_mutate_comptime_var_state(&value->data.x_array.data.s_none.elements[i]))
+                            return true;
+                    }
+                    return false;
             }
-            return false;
-
+            zig_unreachable();
         case ZigTypeIdStruct:
             for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) {
                 if (can_mutate_comptime_var_state(&value->data.x_struct.fields[i]))
@@ -5039,6 +5049,8 @@ uint32_t fn_eval_hash(Scope* scope) {
 }
 
 bool fn_eval_eql(Scope *a, Scope *b) {
+    assert(a->codegen != nullptr);
+    assert(b->codegen != nullptr);
     while (a && b) {
         if (a->id != b->id)
             return false;
@@ -5048,7 +5060,7 @@ bool fn_eval_eql(Scope *a, Scope *b) {
             ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
             if (a_var_scope->var->value->type != b_var_scope->var->value->type)
                 return false;
-            if (!const_values_equal(a_var_scope->var->value, b_var_scope->var->value))
+            if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value))
                 return false;
         } else if (a->id == ScopeIdFnDef) {
             ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
@@ -5130,14 +5142,8 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
 
     const_val->special = ConstValSpecialStatic;
     const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
-    const_val->data.x_array.s_none.elements = create_const_vals(buf_len(str));
-
-    for (size_t i = 0; i < buf_len(str); i += 1) {
-        ConstExprValue *this_char = &const_val->data.x_array.s_none.elements[i];
-        this_char->special = ConstValSpecialStatic;
-        this_char->type = g->builtin_types.entry_u8;
-        bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]);
-    }
+    const_val->data.x_array.special = ConstArraySpecialBuf;
+    const_val->data.x_array.data.s_buf = str;
 
     g->string_literals_table.put(str, const_val);
 }
@@ -5154,14 +5160,15 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
     ConstExprValue *array_val = create_const_vals(1);
     array_val->special = ConstValSpecialStatic;
     array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null);
-    array_val->data.x_array.s_none.elements = create_const_vals(len_with_null);
+    // TODO buf optimization
+    array_val->data.x_array.data.s_none.elements = create_const_vals(len_with_null);
     for (size_t i = 0; i < buf_len(str); i += 1) {
-        ConstExprValue *this_char = &array_val->data.x_array.s_none.elements[i];
+        ConstExprValue *this_char = &array_val->data.x_array.data.s_none.elements[i];
         this_char->special = ConstValSpecialStatic;
         this_char->type = g->builtin_types.entry_u8;
         bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]);
     }
-    ConstExprValue *null_char = &array_val->data.x_array.s_none.elements[len_with_null - 1];
+    ConstExprValue *null_char = &array_val->data.x_array.data.s_none.elements[len_with_null - 1];
     null_char->special = ConstValSpecialStatic;
     null_char->type = g->builtin_types.entry_u8;
     bigint_init_unsigned(&null_char->data.x_bigint, 0);
@@ -5535,7 +5542,7 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
     zig_unreachable();
 }
 
-bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
+bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
     assert(a->type->id == b->type->id);
     assert(a->special == ConstValSpecialStatic);
     assert(b->special == ConstValSpecialStatic);
@@ -5593,13 +5600,20 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
             assert(a->type->data.array.len == b->type->data.array.len);
             assert(a->data.x_array.special != ConstArraySpecialUndef);
             assert(b->data.x_array.special != ConstArraySpecialUndef);
+            if (a->data.x_array.special == ConstArraySpecialBuf &&
+                b->data.x_array.special == ConstArraySpecialBuf)
+            {
+                return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
+            }
+            expand_undef_array(g, a);
+            expand_undef_array(g, b);
 
             size_t len = a->type->data.array.len;
-            ConstExprValue *a_elems = a->data.x_array.s_none.elements;
-            ConstExprValue *b_elems = b->data.x_array.s_none.elements;
+            ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
+            ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
 
             for (size_t i = 0; i < len; ++i) {
-                if (!const_values_equal(&a_elems[i], &b_elems[i]))
+                if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
                     return false;
             }
 
@@ -5609,7 +5623,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
             for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
                 ConstExprValue *field_a = &a->data.x_struct.fields[i];
                 ConstExprValue *field_b = &b->data.x_struct.fields[i];
-                if (!const_values_equal(field_a, field_b))
+                if (!const_values_equal(g, field_a, field_b))
                     return false;
             }
             return true;
@@ -5623,7 +5637,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
             if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
                 return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
             } else {
-                return const_values_equal(a->data.x_optional, b->data.x_optional);
+                return const_values_equal(g, a->data.x_optional, b->data.x_optional);
             }
         case ZigTypeIdErrorUnion:
             zig_panic("TODO");
@@ -5808,26 +5822,15 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
         case ZigTypeIdPointer:
             return render_const_val_ptr(g, buf, const_val, type_entry);
         case ZigTypeIdArray:
-            {
-                ZigType *child_type = type_entry->data.array.child_type;
-                uint64_t len = type_entry->data.array.len;
-
-                if (const_val->data.x_array.special == ConstArraySpecialUndef) {
+            switch (const_val->data.x_array.special) {
+                case ConstArraySpecialUndef:
                     buf_append_str(buf, "undefined");
                     return;
-                }
-
-                // if it's []u8, assume UTF-8 and output a string
-                if (child_type->id == ZigTypeIdInt &&
-                    child_type->data.integral.bit_count == 8 &&
-                    !child_type->data.integral.is_signed)
-                {
+                case ConstArraySpecialBuf: {
+                    Buf *array_buf = const_val->data.x_array.data.s_buf;
                     buf_append_char(buf, '"');
-                    for (uint64_t i = 0; i < len; i += 1) {
-                        ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i];
-                        uint64_t big_c = bigint_as_unsigned(&child_value->data.x_bigint);
-                        assert(big_c <= UINT8_MAX);
-                        uint8_t c = (uint8_t)big_c;
+                    for (size_t i = 0; i < buf_len(array_buf); i += 1) {
+                        uint8_t c = buf_ptr(array_buf)[i];
                         if (c == '"') {
                             buf_append_str(buf, "\\\"");
                         } else {
@@ -5837,17 +5840,20 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
                     buf_append_char(buf, '"');
                     return;
                 }
-
-                buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
-                for (uint64_t i = 0; i < len; i += 1) {
-                    if (i != 0)
-                        buf_appendf(buf, ",");
-                    ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i];
-                    render_const_value(g, buf, child_value);
+                case ConstArraySpecialNone: {
+                    buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
+                    uint64_t len = type_entry->data.array.len;
+                    for (uint64_t i = 0; i < len; i += 1) {
+                        if (i != 0)
+                            buf_appendf(buf, ",");
+                        ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
+                        render_const_value(g, buf, child_value);
+                    }
+                    buf_appendf(buf, "}");
+                    return;
                 }
-                buf_appendf(buf, "}");
-                return;
             }
+            zig_unreachable();
         case ZigTypeIdNull:
             {
                 buf_appendf(buf, "null");
@@ -6102,24 +6108,49 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
     zig_unreachable();
 }
 
+// Canonicalize the array value as ConstArraySpecialNone
 void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
     assert(const_val->type->id == ZigTypeIdArray);
-    if (const_val->data.x_array.special == ConstArraySpecialUndef) {
-        const_val->data.x_array.special = ConstArraySpecialNone;
-        size_t elem_count = const_val->type->data.array.len;
-        const_val->data.x_array.s_none.elements = create_const_vals(elem_count);
-        for (size_t i = 0; i < elem_count; i += 1) {
-            ConstExprValue *element_val = &const_val->data.x_array.s_none.elements[i];
-            element_val->type = const_val->type->data.array.child_type;
-            init_const_undefined(g, element_val);
-            ConstParent *parent = get_const_val_parent(g, element_val);
-            if (parent != nullptr) {
-                parent->id = ConstParentIdArray;
-                parent->data.p_array.array_val = const_val;
-                parent->data.p_array.elem_index = i;
+    switch (const_val->data.x_array.special) {
+        case ConstArraySpecialNone:
+            return;
+        case ConstArraySpecialUndef: {
+            const_val->data.x_array.special = ConstArraySpecialNone;
+            size_t elem_count = const_val->type->data.array.len;
+            const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
+            for (size_t i = 0; i < elem_count; i += 1) {
+                ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
+                element_val->type = const_val->type->data.array.child_type;
+                init_const_undefined(g, element_val);
+                ConstParent *parent = get_const_val_parent(g, element_val);
+                if (parent != nullptr) {
+                    parent->id = ConstParentIdArray;
+                    parent->data.p_array.array_val = const_val;
+                    parent->data.p_array.elem_index = i;
+                }
             }
+            return;
+        }
+        case ConstArraySpecialBuf: {
+            Buf *buf = const_val->data.x_array.data.s_buf;
+            // If we're doing this it means that we are potentially modifying the data,
+            // so we can't have it be in the string literals table
+            g->string_literals_table.maybe_remove(buf);
+
+            const_val->data.x_array.special = ConstArraySpecialNone;
+            size_t elem_count = const_val->type->data.array.len;
+            assert(elem_count == buf_len(buf));
+            const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
+            for (size_t i = 0; i < elem_count; i += 1) {
+                ConstExprValue *this_char = &const_val->data.x_array.data.s_none.elements[i];
+                this_char->special = ConstValSpecialStatic;
+                this_char->type = g->builtin_types.entry_u8;
+                bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
+            }
+            return;
         }
     }
+    zig_unreachable();
 }
 
 ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
@@ -6127,7 +6158,7 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
     ZigType *type_entry = value->type;
     if (type_entry->id == ZigTypeIdArray) {
         expand_undef_array(g, value);
-        return &value->data.x_array.s_none.parent;
+        return &value->data.x_array.data.s_none.parent;
     } else if (type_entry->id == ZigTypeIdStruct) {
         return &value->data.x_struct.parent;
     } else if (type_entry->id == ZigTypeIdUnion) {
src/analyze.hpp
@@ -84,8 +84,8 @@ void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source
 ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
     bool is_const, ConstExprValue *init_value, Tld *src_tld);
 ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node);
-ZigFn *create_fn(AstNode *proto_node);
-ZigFn *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage);
+ZigFn *create_fn(CodeGen *g, AstNode *proto_node);
+ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value);
 void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc);
 AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index);
 bool type_requires_comptime(ZigType *type_entry);
@@ -93,25 +93,25 @@ Error ATTRIBUTE_MUST_USE ensure_complete_type(CodeGen *g, ZigType *type_entry);
 Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status);
 void complete_enum(CodeGen *g, ZigType *enum_type);
 bool ir_get_var_is_comptime(ZigVar *var);
-bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
+bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b);
 void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_val, bool is_max);
 void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max);
 
 void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val);
 void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node);
 
-ScopeBlock *create_block_scope(AstNode *node, Scope *parent);
-ScopeDefer *create_defer_scope(AstNode *node, Scope *parent);
-ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
-Scope *create_var_scope(AstNode *node, Scope *parent, ZigVar *var);
-ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
-ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
-ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent);
-ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, ZigFn *fn_entry);
-ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import);
-Scope *create_comptime_scope(AstNode *node, Scope *parent);
-Scope *create_coro_prelude_scope(AstNode *node, Scope *parent);
-Scope *create_runtime_scope(AstNode *node, Scope *parent, IrInstruction *is_comptime);
+ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent);
+ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent);
+ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
+Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var);
+ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent);
+ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent);
+ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
+ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
+ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import);
+Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
+Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent);
+Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);
 
 void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
 ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
src/codegen.cpp
@@ -5336,7 +5336,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
 
 static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) {
     expand_undef_array(g, array_const_val);
-    ConstParent *parent = &array_const_val->data.x_array.s_none.parent;
+    ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent;
     LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent);
 
     LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr)));
@@ -5716,23 +5716,29 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
         case ZigTypeIdArray:
             {
                 uint64_t len = type_entry->data.array.len;
-                if (const_val->data.x_array.special == ConstArraySpecialUndef) {
-                    return LLVMGetUndef(type_entry->type_ref);
-                }
-
-                LLVMValueRef *values = allocate<LLVMValueRef>(len);
-                LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
-                bool make_unnamed_struct = false;
-                for (uint64_t i = 0; i < len; i += 1) {
-                    ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
-                    LLVMValueRef val = gen_const_val(g, elem_value, "");
-                    values[i] = val;
-                    make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
-                }
-                if (make_unnamed_struct) {
-                    return LLVMConstStruct(values, len, true);
-                } else {
-                    return LLVMConstArray(element_type_ref, values, (unsigned)len);
+                switch (const_val->data.x_array.special) {
+                    case ConstArraySpecialUndef:
+                        return LLVMGetUndef(type_entry->type_ref);
+                    case ConstArraySpecialNone: {
+                        LLVMValueRef *values = allocate<LLVMValueRef>(len);
+                        LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
+                        bool make_unnamed_struct = false;
+                        for (uint64_t i = 0; i < len; i += 1) {
+                            ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
+                            LLVMValueRef val = gen_const_val(g, elem_value, "");
+                            values[i] = val;
+                            make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
+                        }
+                        if (make_unnamed_struct) {
+                            return LLVMConstStruct(values, len, true);
+                        } else {
+                            return LLVMConstArray(element_type_ref, values, (unsigned)len);
+                        }
+                    }
+                    case ConstArraySpecialBuf: {
+                        Buf *buf = const_val->data.x_array.data.s_buf;
+                        return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
+                    }
                 }
             }
         case ZigTypeIdUnion:
@@ -7278,7 +7284,7 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) {
     import->source_code = nullptr;
     import->path = full_path;
     g->root_import = import;
-    import->decls_scope = create_decls_scope(nullptr, nullptr, nullptr, import);
+    import->decls_scope = create_decls_scope(g, nullptr, nullptr, nullptr, import);
 
     init(g);
 
@@ -7352,12 +7358,12 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
     ConstExprValue *test_fn_array = create_const_vals(1);
     test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length);
     test_fn_array->special = ConstValSpecialStatic;
-    test_fn_array->data.x_array.s_none.elements = create_const_vals(g->test_fns.length);
+    test_fn_array->data.x_array.data.s_none.elements = create_const_vals(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);
 
-        ConstExprValue *this_val = &test_fn_array->data.x_array.s_none.elements[i];
+        ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i];
         this_val->special = ConstValSpecialStatic;
         this_val->type = struct_type;
         this_val->data.x_struct.parent.id = ConstParentIdArray;
src/ir.cpp
@@ -166,7 +166,7 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c
             break;
         case ConstPtrSpecialBaseArray:
             expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val);
-            result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
+            result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[
                 const_val->data.x_ptr.data.base_array.elem_index];
             break;
         case ConstPtrSpecialBaseStruct:
@@ -3360,7 +3360,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
     variable_entry->src_is_const = src_is_const;
     variable_entry->gen_is_const = gen_is_const;
     variable_entry->decl_node = node;
-    variable_entry->child_scope = create_var_scope(node, parent_scope, variable_entry);
+    variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry);
 
     return variable_entry;
 }
@@ -3388,7 +3388,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
     ZigList<IrInstruction *> incoming_values = {0};
     ZigList<IrBasicBlock *> incoming_blocks = {0};
 
-    ScopeBlock *scope_block = create_block_scope(block_node, parent_scope);
+    ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
 
     Scope *outer_block_scope = &scope_block->base;
     Scope *child_scope = outer_block_scope;
@@ -5026,7 +5026,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode
 
     ir_set_cursor_at_end_and_append_block(irb, then_block);
 
-    Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
     IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope);
     if (then_expr_result == irb->codegen->invalid_instruction)
         return then_expr_result;
@@ -5318,7 +5318,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
         ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
     ir_build_br(irb, scope, node, cond_block, is_comptime);
 
-    Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
     Buf *var_symbol = node->data.while_expr.var_symbol;
     Buf *err_symbol = node->data.while_expr.err_symbol;
     if (err_symbol != nullptr) {
@@ -5359,7 +5359,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
         ZigList<IrInstruction *> incoming_values = {0};
         ZigList<IrBasicBlock *> incoming_blocks = {0};
 
-        ScopeLoop *loop_scope = create_loop_scope(node, payload_scope);
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
         loop_scope->break_block = end_block;
         loop_scope->continue_block = continue_block;
         loop_scope->is_comptime = is_comptime;
@@ -5415,7 +5415,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
         return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
     } else if (var_symbol != nullptr) {
         ir_set_cursor_at_end_and_append_block(irb, cond_block);
-        Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
         // TODO make it an error to write to payload variable
         AstNode *symbol_node = node; // TODO make more accurate
 
@@ -5443,7 +5443,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
         ZigList<IrInstruction *> incoming_values = {0};
         ZigList<IrBasicBlock *> incoming_blocks = {0};
 
-        ScopeLoop *loop_scope = create_loop_scope(node, child_scope);
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
         loop_scope->break_block = end_block;
         loop_scope->continue_block = continue_block;
         loop_scope->is_comptime = is_comptime;
@@ -5506,9 +5506,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
         ZigList<IrInstruction *> incoming_values = {0};
         ZigList<IrBasicBlock *> incoming_blocks = {0};
 
-        Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
 
-        ScopeLoop *loop_scope = create_loop_scope(node, subexpr_scope);
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
         loop_scope->break_block = end_block;
         loop_scope->continue_block = continue_block;
         loop_scope->is_comptime = is_comptime;
@@ -5645,7 +5645,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
 
     ZigList<IrInstruction *> incoming_values = {0};
     ZigList<IrBasicBlock *> incoming_blocks = {0};
-    ScopeLoop *loop_scope = create_loop_scope(node, child_scope);
+    ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
     loop_scope->break_block = end_block;
     loop_scope->continue_block = continue_block;
     loop_scope->is_comptime = is_comptime;
@@ -5855,7 +5855,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no
 
     ir_set_cursor_at_end_and_append_block(irb, then_block);
 
-    Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
     Scope *var_scope;
     if (var_symbol) {
         IrInstruction *var_type = nullptr;
@@ -5930,7 +5930,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
 
     ir_set_cursor_at_end_and_append_block(irb, ok_block);
 
-    Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
     Scope *var_scope;
     if (var_symbol) {
         IrInstruction *var_type = nullptr;
@@ -6066,8 +6066,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
     ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0};
 
     // First do the else and the ranges
-    Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime);
-    Scope *comptime_scope = create_comptime_scope(node, scope);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
     AstNode *else_prong = nullptr;
     for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
         AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
@@ -6231,7 +6231,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
 static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
     assert(node->type == NodeTypeCompTime);
 
-    Scope *child_scope = create_comptime_scope(node, parent_scope);
+    Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
     return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
 }
 
@@ -6394,10 +6394,10 @@ static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *n
 static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
     assert(node->type == NodeTypeDefer);
 
-    ScopeDefer *defer_child_scope = create_defer_scope(node, parent_scope);
+    ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope);
     node->data.defer.child_scope = &defer_child_scope->base;
 
-    ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(node, parent_scope);
+    ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope);
     node->data.defer.expr_scope = &defer_expr_scope->base;
 
     return ir_build_const_void(irb, parent_scope, node);
@@ -7154,7 +7154,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
         suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
     } else {
         Scope *child_scope;
-        ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope);
+        ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
         suspend_scope->resume_block = resume_block;
         child_scope = &suspend_scope->base;
         IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
@@ -7370,7 +7370,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
     ZigVar *coro_size_var;
     if (is_async) {
         // create the coro promise
-        Scope *coro_scope = create_coro_prelude_scope(node, scope);
+        Scope *coro_scope = create_coro_prelude_scope(irb->codegen, node, scope);
         const_bool_false = ir_build_const_bool(irb, coro_scope, node, false);
         ZigVar *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
 
@@ -10569,9 +10569,9 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou
             array_val->special = ConstValSpecialStatic;
             array_val->type = array_type;
             array_val->data.x_array.special = ConstArraySpecialNone;
-            array_val->data.x_array.s_none.elements = pointee;
-            array_val->data.x_array.s_none.parent.id = ConstParentIdScalar;
-            array_val->data.x_array.s_none.parent.data.p_scalar.scalar_val = pointee;
+            array_val->data.x_array.data.s_none.elements = pointee;
+            array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar;
+            array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee;
 
             IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
                     source_instr->scope, source_instr->source_node);
@@ -11391,13 +11391,16 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
 
     assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
     ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
+    if (array_val->data.x_array.special == ConstArraySpecialBuf) {
+        return array_val->data.x_array.data.s_buf;
+    }
     expand_undef_array(ira->codegen, array_val);
     size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
     Buf *result = buf_alloc();
     buf_resize(result, len);
     for (size_t i = 0; i < len; i += 1) {
         size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
-        ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index];
+        ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
         if (char_val->special == ConstValSpecialUndef) {
             ir_add_error(ira, casted_value, buf_sprintf("use of undefined value"));
             return nullptr;
@@ -11750,7 +11753,7 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op
             Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
             answer = resolve_cmp_op_id(op_id, cmp_result);
         } else {
-            bool are_equal = one_possible_value || const_values_equal(op1_val, op2_val);
+            bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val);
             if (op_id == IrBinOpCmpEq) {
                 answer = are_equal;
             } else if (op_id == IrBinOpCmpNotEq) {
@@ -12463,19 +12466,20 @@ static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruc
         return result_type;
     }
 
-    out_array_val->data.x_array.s_none.elements = create_const_vals(new_len);
+    out_array_val->data.x_array.data.s_none.elements = create_const_vals(new_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);
 
     size_t next_index = 0;
     for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
-        out_array_val->data.x_array.s_none.elements[next_index] = op1_array_val->data.x_array.s_none.elements[i];
+        out_array_val->data.x_array.data.s_none.elements[next_index] = op1_array_val->data.x_array.data.s_none.elements[i];
     }
     for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
-        out_array_val->data.x_array.s_none.elements[next_index] = op2_array_val->data.x_array.s_none.elements[i];
+        out_array_val->data.x_array.data.s_none.elements[next_index] = op2_array_val->data.x_array.data.s_none.elements[i];
     }
     if (next_index < new_len) {
-        ConstExprValue *null_byte = &out_array_val->data.x_array.s_none.elements[next_index];
+        ConstExprValue *null_byte = &out_array_val->data.x_array.data.s_none.elements[next_index];
         init_const_unsigned_negative(null_byte, child_type, 0, false);
         next_index += 1;
     }
@@ -12524,12 +12528,14 @@ static ZigType *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instru
         return get_array_type(ira->codegen, child_type, new_array_len);
     }
 
-    out_val->data.x_array.s_none.elements = create_const_vals(new_array_len);
+    // TODO optimize the buf case
+    expand_undef_array(ira->codegen, array_val);
+    out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);
 
     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) {
-            out_val->data.x_array.s_none.elements[i] = array_val->data.x_array.s_none.elements[y];
+            out_val->data.x_array.data.s_none.elements[i] = array_val->data.x_array.data.s_none.elements[y];
             i += 1;
         }
     }
@@ -13502,10 +13508,10 @@ static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instr
 
         // 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(fn_proto_node);
+        ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node);
         impl_fn->param_source_nodes = 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(impl_fn->body_node, parent_scope, impl_fn);
+        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;
         FnTypeId inst_fn_type_id = {0};
         init_fn_type_id(&inst_fn_type_id, fn_proto_node, new_fn_arg_count);
@@ -16073,7 +16079,7 @@ static ZigType *ir_analyze_instruction_switch_br(IrAnalyze *ira,
             if (!case_val)
                 return ir_unreach_error(ira);
 
-            if (const_values_equal(target_val, case_val)) {
+            if (const_values_equal(ira->codegen, target_val, case_val)) {
                 old_dest_block = old_case->block;
                 break;
             }
@@ -16652,7 +16658,7 @@ static ZigType *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
             ConstExprValue const_val = {};
             const_val.special = ConstValSpecialStatic;
             const_val.type = fixed_size_array_type;
-            const_val.data.x_array.s_none.elements = create_const_vals(elem_count);
+            const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count);
 
             bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
 
@@ -16677,7 +16683,7 @@ static ZigType *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
                         if (!elem_val)
                             return ira->codegen->builtin_types.entry_invalid;
 
-                        copy_const_val(&const_val.data.x_array.s_none.elements[i], elem_val, true);
+                        copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true);
                     } else {
                         first_non_const_instruction = casted_arg;
                         const_val.special = ConstValSpecialRuntime;
@@ -16689,7 +16695,7 @@ static ZigType *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
                 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
                 *out_val = const_val;
                 for (size_t i = 0; i < elem_count; i += 1) {
-                    ConstExprValue *elem_val = &out_val->data.x_array.s_none.elements[i];
+                    ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i];
                     ConstParent *parent = get_const_val_parent(ira->codegen, elem_val);
                     if (parent != nullptr) {
                         parent->id = ConstParentIdArray;
@@ -17146,8 +17152,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
     definition_array->special = ConstValSpecialStatic;
     definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count);
     definition_array->data.x_array.special = ConstArraySpecialNone;
-    definition_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-    definition_array->data.x_array.s_none.elements = create_const_vals(definition_count);
+    definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+    definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count);
     init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false);
 
     // Loop through the definitions and generate info.
@@ -17164,7 +17170,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
                 continue;
         }
 
-        ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index];
+        ConstExprValue *definition_val = &definition_array->data.x_array.data.s_none.elements[definition_index];
 
         definition_val->special = ConstValSpecialStatic;
         definition_val->type = type_info_definition_type;
@@ -17293,15 +17299,15 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
                     fn_arg_name_array->type = get_array_type(ira->codegen,
                             get_slice_type(ira->codegen, u8_ptr), fn_arg_count);
                     fn_arg_name_array->data.x_array.special = ConstArraySpecialNone;
-                    fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                    fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count);
+                    fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                    fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
 
                     init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false);
 
                     for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++)
                     {
                         ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index);
-                        ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.s_none.elements[fn_arg_index];
+                        ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index];
                         ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name);
                         init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true);
                         fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray;
@@ -17593,15 +17599,15 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
                 enum_field_array->special = ConstValSpecialStatic;
                 enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count);
                 enum_field_array->data.x_array.special = ConstArraySpecialNone;
-                enum_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                enum_field_array->data.x_array.s_none.elements = create_const_vals(enum_field_count);
+                enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count);
 
                 init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false);
 
                 for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++)
                 {
                     TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
-                    ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index];
+                    ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index];
                     make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type);
                     enum_field_val->data.x_struct.parent.id = ConstParentIdArray;
                     enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array;
@@ -17632,13 +17638,13 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
                 error_array->special = ConstValSpecialStatic;
                 error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count);
                 error_array->data.x_array.special = ConstArraySpecialNone;
-                error_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                error_array->data.x_array.s_none.elements = create_const_vals(error_count);
+                error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                error_array->data.x_array.data.s_none.elements = create_const_vals(error_count);
 
                 init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false);
                 for (uint32_t error_index = 0; error_index < error_count; error_index++) {
                     ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
-                    ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index];
+                    ConstExprValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index];
 
                     error_val->special = ConstValSpecialStatic;
                     error_val->type = type_info_error_type;
@@ -17727,8 +17733,8 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
                 union_field_array->special = ConstValSpecialStatic;
                 union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count);
                 union_field_array->data.x_array.special = ConstArraySpecialNone;
-                union_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                union_field_array->data.x_array.s_none.elements = create_const_vals(union_field_count);
+                union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count);
 
                 init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);
 
@@ -17736,7 +17742,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
 
                 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
                     TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
-                    ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];
+                    ConstExprValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index];
 
                     union_field_val->special = ConstValSpecialStatic;
                     union_field_val->type = type_info_union_field_type;
@@ -17800,14 +17806,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
                 struct_field_array->special = ConstValSpecialStatic;
                 struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count);
                 struct_field_array->data.x_array.special = ConstArraySpecialNone;
-                struct_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                struct_field_array->data.x_array.s_none.elements = create_const_vals(struct_field_count);
+                struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count);
 
                 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
 
                 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
                     TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
-                    ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];
+                    ConstExprValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index];
 
                     struct_field_val->special = ConstValSpecialStatic;
                     struct_field_val->type = type_info_struct_field_type;
@@ -17906,15 +17912,15 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
                 fn_arg_array->special = ConstValSpecialStatic;
                 fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count);
                 fn_arg_array->data.x_array.special = ConstArraySpecialNone;
-                fn_arg_array->data.x_array.s_none.parent.id = ConstParentIdNone;
-                fn_arg_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count);
+                fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone;
+                fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
 
                 init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false);
 
                 for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++)
                 {
                     FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index];
-                    ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.s_none.elements[fn_arg_index];
+                    ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index];
 
                     fn_arg_val->special = ConstValSpecialStatic;
                     fn_arg_val->type = type_info_fn_arg_type;
@@ -18059,7 +18065,7 @@ static ZigType *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCIm
     assert(node->type == NodeTypeFnCallExpr);
     AstNode *block_node = node->data.fn_call_expr.params.at(0);
 
-    ScopeCImport *cimport_scope = create_cimport_scope(node, instruction->base.scope);
+    ScopeCImport *cimport_scope = create_cimport_scope(ira->codegen, node, instruction->base.scope);
 
     // Execute the C import block like an inline function
     ZigType *void_type = ira->codegen->builtin_types.entry_void;
@@ -18072,7 +18078,7 @@ static ZigType *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCIm
     find_libc_include_path(ira->codegen);
 
     ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
-    child_import->decls_scope = create_decls_scope(node, nullptr, nullptr, child_import);
+    child_import->decls_scope = create_decls_scope(ira->codegen, node, nullptr, nullptr, child_import);
     child_import->c_import_node = node;
     child_import->package = new_anonymous_package();
     child_import->package->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package);
@@ -18826,7 +18832,7 @@ static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemse
                 {
                     ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
                     expand_undef_array(ira->codegen, array_val);
-                    dest_elements = array_val->data.x_array.s_none.elements;
+                    dest_elements = array_val->data.x_array.data.s_none.elements;
                     start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
                     bound_end = array_val->type->data.array.len;
                     break;
@@ -18940,7 +18946,7 @@ static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcp
                 {
                     ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
                     expand_undef_array(ira->codegen, array_val);
-                    dest_elements = array_val->data.x_array.s_none.elements;
+                    dest_elements = array_val->data.x_array.data.s_none.elements;
                     dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
                     dest_end = array_val->type->data.array.len;
                     break;
@@ -18976,7 +18982,7 @@ static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcp
                 {
                     ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val;
                     expand_undef_array(ira->codegen, array_val);
-                    src_elements = array_val->data.x_array.s_none.elements;
+                    src_elements = array_val->data.x_array.data.s_none.elements;
                     src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index;
                     src_end = array_val->type->data.array.len;
                     break;
@@ -20282,9 +20288,10 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
         case ZigTypeIdArray:
             {
                 size_t buf_i = 0;
+                // TODO optimize the buf case
                 expand_undef_array(codegen, val);
                 for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
-                    ConstExprValue *elem = &val->data.x_array.s_none.elements[elem_i];
+                    ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
                     buf_write_value_bytes(codegen, &buf[buf_i], elem);
                     buf_i += type_size(codegen, elem->type);
                 }
test/cases/bugs/1076.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const mem = std.mem;
+const assert = std.debug.assert;
+
+test "comptime code should not modify constant data" {
+    testCastPtrOfArrayToSliceAndPtr();
+    comptime testCastPtrOfArrayToSliceAndPtr();
+}
+
+fn testCastPtrOfArrayToSliceAndPtr() void {
+    var array = "aoeu";
+    const x: [*]u8 = &array;
+    x[0] += 1;
+    assert(mem.eql(u8, array[0..], "boeu"));
+}
+
test/cases/cast.zig
@@ -400,7 +400,7 @@ test "single-item pointer of array to slice and to unknown length pointer" {
 }
 
 fn testCastPtrOfArrayToSliceAndPtr() void {
-    var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076
+    var array = "aoeu";
     const x: [*]u8 = &array;
     x[0] += 1;
     assert(mem.eql(u8, array[0..], "boeu"));
test/behavior.zig
@@ -8,6 +8,7 @@ comptime {
     _ = @import("cases/atomics.zig");
     _ = @import("cases/bitcast.zig");
     _ = @import("cases/bool.zig");
+    _ = @import("cases/bugs/1076.zig");
     _ = @import("cases/bugs/1111.zig");
     _ = @import("cases/bugs/1277.zig");
     _ = @import("cases/bugs/1322.zig");