diff --git a/src/Air.zig b/src/Air.zig index b291dbee7a..d1c6d184e6 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1034,7 +1034,12 @@ pub const Inst = struct { ty: Type, arg: struct { ty: Ref, - src_index: u32, + /// Index into `extra` of a null-terminated string representing the parameter name. + /// This is `.none` if debug info is stripped. + name: enum(u32) { + none = std.math.maxInt(u32), + _, + }, }, ty_op: struct { ty: Ref, diff --git a/src/Compilation.zig b/src/Compilation.zig index 118e325ed7..cc5fd1a9eb 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1877,6 +1877,7 @@ pub fn destroy(comp: *Compilation) void { if (comp.module) |zcu| zcu.deinit(); comp.cache_use.deinit(); comp.work_queue.deinit(); + if (!InternPool.single_threaded) comp.codegen_work.queue.deinit(); comp.c_object_work_queue.deinit(); if (!build_options.only_core_functionality) { comp.win32_resource_work_queue.deinit(); @@ -2119,12 +2120,14 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } if (comp.module) |zcu| { + const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main }; + zcu.compile_log_text.shrinkAndFree(gpa, 0); // Make sure std.zig is inside the import_table. We unconditionally need // it for start.zig. const std_mod = zcu.std_mod; - _ = try zcu.importPkg(std_mod); + _ = try pt.importPkg(std_mod); // Normally we rely on importing std to in turn import the root source file // in the start code, but when using the stage1 backend that won't happen, @@ -2133,20 +2136,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { // Likewise, in the case of `zig test`, the test runner is the root source file, // and so there is nothing to import the main file. if (comp.config.is_test) { - _ = try zcu.importPkg(zcu.main_mod); + _ = try pt.importPkg(zcu.main_mod); } if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { - _ = try zcu.importPkg(compiler_rt_mod); + _ = try pt.importPkg(compiler_rt_mod); } // Put a work item in for every known source file to detect if // it changed, and, if so, re-compute ZIR and then queue the job // to update it. try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count()); - for (zcu.import_table.values(), 0..) |file, file_index_usize| { - const file_index: Zcu.File.Index = @enumFromInt(file_index_usize); - if (file.mod.isBuiltin()) continue; + for (zcu.import_table.values()) |file_index| { + if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue; comp.astgen_work_queue.writeItemAssumeCapacity(file_index); } @@ -2641,7 +2643,8 @@ fn resolveEmitLoc( return slice.ptr; } -fn reportMultiModuleErrors(zcu: *Zcu) !void { +fn reportMultiModuleErrors(pt: Zcu.PerThread) !void { + const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to @@ -2651,14 +2654,13 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { // Attach the "some omitted" note to the final error message var last_err: ?*Zcu.ErrorMsg = null; - for (zcu.import_table.values(), 0..) |file, file_index_usize| { + for (zcu.import_table.values()) |file_index| { + const file = zcu.fileByIndex(file_index); if (!file.multi_pkg) continue; num_errors += 1; if (num_errors > max_errors) continue; - const file_index: Zcu.File.Index = @enumFromInt(file_index_usize); - const err = err_blk: { // Like with errors, let's cap the number of notes to prevent a huge error spew. const max_notes = 5; @@ -2674,7 +2676,10 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { .import => |import| try Zcu.ErrorMsg.init( gpa, .{ - .base_node_inst = try ip.trackZir(gpa, import.file, .main_struct_inst), + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = import.file, + .inst = .main_struct_inst, + }), .offset = .{ .token_abs = import.token }, }, "imported from module {s}", @@ -2683,7 +2688,10 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { .root => |pkg| try Zcu.ErrorMsg.init( gpa, .{ - .base_node_inst = try ip.trackZir(gpa, file_index, .main_struct_inst), + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }), .offset = .entire_file, }, "root of module {s}", @@ -2697,7 +2705,10 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { notes[num_notes] = try Zcu.ErrorMsg.init( gpa, .{ - .base_node_inst = try ip.trackZir(gpa, file_index, .main_struct_inst), + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }), .offset = .entire_file, }, "{} more references omitted", @@ -2709,7 +2720,10 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { const err = try Zcu.ErrorMsg.create( gpa, .{ - .base_node_inst = try ip.trackZir(gpa, file_index, .main_struct_inst), + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }), .offset = .entire_file, }, "file exists in multiple modules", @@ -2749,8 +2763,9 @@ fn reportMultiModuleErrors(zcu: *Zcu) !void { // to add this flag after reporting the errors however, as otherwise // we'd get an error for every single downstream file, which wouldn't be // very useful. - for (zcu.import_table.values()) |file| { - if (file.multi_pkg) file.recursiveMarkMultiPkg(zcu); + for (zcu.import_table.values()) |file_index| { + const file = zcu.fileByIndex(file_index); + if (file.multi_pkg) file.recursiveMarkMultiPkg(pt); } } @@ -2774,7 +2789,7 @@ const Header = extern struct { //extra_len: u32, //limbs_len: u32, //string_bytes_len: u32, - tracked_insts_len: u32, + //tracked_insts_len: u32, src_hash_deps_len: u32, decl_val_deps_len: u32, namespace_deps_len: u32, @@ -2782,7 +2797,7 @@ const Header = extern struct { first_dependency_len: u32, dep_entries_len: u32, free_dep_entries_len: u32, - files_len: u32, + //files_len: u32, }, }; @@ -2803,7 +2818,7 @@ pub fn saveState(comp: *Compilation) !void { //.extra_len = @intCast(ip.extra.items.len), //.limbs_len = @intCast(ip.limbs.items.len), //.string_bytes_len = @intCast(ip.string_bytes.items.len), - .tracked_insts_len = @intCast(ip.tracked_insts.count()), + //.tracked_insts_len = @intCast(ip.tracked_insts.count()), .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), .decl_val_deps_len = @intCast(ip.decl_val_deps.count()), .namespace_deps_len = @intCast(ip.namespace_deps.count()), @@ -2811,7 +2826,7 @@ pub fn saveState(comp: *Compilation) !void { .first_dependency_len = @intCast(ip.first_dependency.count()), .dep_entries_len = @intCast(ip.dep_entries.items.len), .free_dep_entries_len = @intCast(ip.free_dep_entries.items.len), - .files_len = @intCast(ip.files.entries.len), + //.files_len = @intCast(ip.files.entries.len), }, }; addBuf(&bufs_list, &bufs_len, mem.asBytes(&header)); @@ -2820,7 +2835,7 @@ pub fn saveState(comp: *Compilation) !void { //addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.data))); //addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.tag))); //addBuf(&bufs_list, &bufs_len, ip.string_bytes.items); - addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys())); + //addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys())); addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.keys())); addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.values())); @@ -2836,8 +2851,8 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.dep_entries.items)); addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.free_dep_entries.items)); - addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.files.keys())); - addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.files.values())); + //addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.files.keys())); + //addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.files.values())); // TODO: compilation errors // TODO: namespaces @@ -2929,7 +2944,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { } } - if (zcu.global_error_set.entries.len - 1 > zcu.error_limit) { + if (zcu.intern_pool.global_error_set.mutate.list.len > zcu.error_limit) { total += 1; } } @@ -3058,7 +3073,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { try addModuleErrorMsg(zcu, &bundle, value.*, &all_references); } - const actual_error_count = zcu.global_error_set.entries.len - 1; + const actual_error_count = zcu.intern_pool.global_error_set.mutate.list.len; if (actual_error_count > zcu.error_limit) { try bundle.addRootErrorMessage(.{ .msg = try bundle.printString("ZCU used more errors than possible: used {d}, max {d}", .{ @@ -3443,11 +3458,12 @@ fn performAllTheWorkInner( } } - if (comp.module) |mod| { - try reportMultiModuleErrors(mod); - try mod.flushRetryableFailures(); - mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); - mod.codegen_prog_node = main_progress_node.start("Code Generation", 0); + if (comp.module) |zcu| { + const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = .main }; + try reportMultiModuleErrors(pt); + try zcu.flushRetryableFailures(); + zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); + zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); } if (!InternPool.single_threaded) comp.thread_pool.spawnWgId(&comp.work_queue_wait_group, codegenThread, .{comp}); @@ -4131,14 +4147,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye }; } -const AstGenSrc = union(enum) { - root, - import: struct { - importing_file: Zcu.File.Index, - import_tok: std.zig.Ast.TokenIndex, - }, -}; - fn workerAstGenFile( tid: usize, comp: *Compilation, @@ -4148,7 +4156,7 @@ fn workerAstGenFile( root_decl: Zcu.Decl.OptionalIndex, prog_node: std.Progress.Node, wg: *WaitGroup, - src: AstGenSrc, + src: Zcu.AstGenSrc, ) void { const child_prog_node = prog_node.start(file.sub_file_path, 0); defer child_prog_node.end(); @@ -4158,7 +4166,7 @@ fn workerAstGenFile( error.AnalysisFail => return, else => { file.status = .retryable_failure; - comp.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) { + pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) { // Swallowing this error is OK because it's implied to be OOM when // there is a missing `failed_files` error message. error.OutOfMemory => {}, @@ -4189,9 +4197,9 @@ fn workerAstGenFile( comp.mutex.lock(); defer comp.mutex.unlock(); - const res = pt.zcu.importFile(file, import_path) catch continue; + const res = pt.importFile(file, import_path) catch continue; if (!res.is_pkg) { - res.file.addReference(pt.zcu.*, .{ .import = .{ + res.file.addReference(pt.zcu, .{ .import = .{ .file = file_index, .token = item.data.token, } }) catch continue; @@ -4204,7 +4212,7 @@ fn workerAstGenFile( log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ file.sub_file_path, import_path, import_result.file.sub_file_path, }); - const sub_src: AstGenSrc = .{ .import = .{ + const sub_src: Zcu.AstGenSrc = .{ .import = .{ .importing_file = file_index, .import_tok = item.data.token, } }; @@ -4557,41 +4565,6 @@ fn reportRetryableWin32ResourceError( } } -fn reportRetryableAstGenError( - comp: *Compilation, - src: AstGenSrc, - file_index: Zcu.File.Index, - err: anyerror, -) error{OutOfMemory}!void { - const zcu = comp.module.?; - const gpa = zcu.gpa; - - const file = zcu.fileByIndex(file_index); - file.status = .retryable_failure; - - const src_loc: Zcu.LazySrcLoc = switch (src) { - .root => .{ - .base_node_inst = try zcu.intern_pool.trackZir(gpa, file_index, .main_struct_inst), - .offset = .entire_file, - }, - .import => |info| .{ - .base_node_inst = try zcu.intern_pool.trackZir(gpa, info.importing_file, .main_struct_inst), - .offset = .{ .token_abs = info.import_tok }, - }, - }; - - const err_msg = try Zcu.ErrorMsg.create(gpa, src_loc, "unable to load '{}{s}': {s}", .{ - file.mod.root, file.sub_file_path, @errorName(err), - }); - errdefer err_msg.destroy(gpa); - - { - comp.mutex.lock(); - defer comp.mutex.unlock(); - try zcu.failed_files.putNoClobber(gpa, file, err_msg); - } -} - fn reportRetryableEmbedFileError( comp: *Compilation, embed_file: *Zcu.EmbedFile, diff --git a/src/InternPool.zig b/src/InternPool.zig index 6f7bb17b14..2b21b25a1d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1,12 +1,13 @@ //! All interned objects have both a value and a type. -//! This data structure is self-contained, with the following exceptions: -//! * Module.Namespace has a pointer to Module.File +//! This data structure is self-contained. /// One item per thread, indexed by `tid`, which is dense and unique per thread. locals: []Local = &.{}, /// Length must be a power of two and represents the number of simultaneous /// writers that can mutate any single sharded data structure. shards: []Shard = &.{}, +/// Key is the error name, index is the error tag value. Index 0 has a length-0 string. +global_error_set: GlobalErrorSet = GlobalErrorSet.empty, /// Cached number of active bits in a `tid`. tid_width: if (single_threaded) u0 else std.math.Log2Int(u32) = 0, /// Cached shift amount to put a `tid` in the top bits of a 31-bit value. @@ -14,17 +15,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32) = if (single_th /// Cached shift amount to put a `tid` in the top bits of a 32-bit value. tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32) = if (single_threaded) 0 else 31, -/// Some types such as enums, structs, and unions need to store mappings from field names -/// to field index, or value to field index. In such cases, they will store the underlying -/// field names and values directly, relying on one of these maps, stored separately, -/// to provide lookup. -/// These are not serialized; it is computed upon deserialization. -maps: std.ArrayListUnmanaged(FieldMap) = .{}, - -/// An index into `tracked_insts` gives a reference to a single ZIR instruction which -/// persists across incremental updates. -tracked_insts: std.AutoArrayHashMapUnmanaged(TrackedInst, void) = .{}, - /// Dependencies on the source code hash associated with a ZIR instruction. /// * For a `declaration`, this is the entire declaration body. /// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations). @@ -60,17 +50,6 @@ dep_entries: std.ArrayListUnmanaged(DepEntry) = .{}, /// garbage collection pass. free_dep_entries: std.ArrayListUnmanaged(DepEntry.Index) = .{}, -/// Elements are ordered identically to the `import_table` field of `Zcu`. -/// -/// Unlike `import_table`, this data is serialized as part of incremental -/// compilation state. -/// -/// Key is the hash of the path to this file, used to store -/// `InternPool.TrackedInst`. -/// -/// Value is the `Decl` of the struct that represents this `File`. -files: std.AutoArrayHashMapUnmanaged(Cache.BinDigest, OptionalDeclIndex) = .{}, - /// Whether a multi-threaded intern pool is useful. /// Currently `false` until the intern pool is actually accessed /// from multiple threads to reduce the cost of this data structure. @@ -79,10 +58,6 @@ const want_multi_threaded = false; /// Whether a single-threaded intern pool impl is in use. pub const single_threaded = builtin.single_threaded or !want_multi_threaded; -pub const FileIndex = enum(u32) { - _, -}; - pub const TrackedInst = extern struct { file: FileIndex, inst: Zir.Inst.Index, @@ -92,12 +67,15 @@ pub const TrackedInst = extern struct { } pub const Index = enum(u32) { _, - pub fn resolveFull(i: TrackedInst.Index, ip: *const InternPool) TrackedInst { - return ip.tracked_insts.keys()[@intFromEnum(i)]; + pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) TrackedInst { + const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip); + const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire(); + return tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index]; } pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Zir.Inst.Index { return i.resolveFull(ip).inst; } + pub fn toOptional(i: TrackedInst.Index) Optional { return @enumFromInt(@intFromEnum(i)); } @@ -111,21 +89,124 @@ pub const TrackedInst = extern struct { }; } }; + + pub const Unwrapped = struct { + tid: Zcu.PerThread.Id, + index: u32, + + pub fn wrap(unwrapped: Unwrapped, ip: *const InternPool) TrackedInst.Index { + assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); + assert(unwrapped.index <= ip.getIndexMask(u32)); + return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + unwrapped.index); + } + }; + pub fn unwrap(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) Unwrapped { + return .{ + .tid = @enumFromInt(@intFromEnum(tracked_inst_index) >> ip.tid_shift_32 & ip.getTidMask()), + .index = @intFromEnum(tracked_inst_index) & ip.getIndexMask(u32), + }; + } }; }; pub fn trackZir( ip: *InternPool, gpa: Allocator, - file: FileIndex, - inst: Zir.Inst.Index, + tid: Zcu.PerThread.Id, + key: TrackedInst, ) Allocator.Error!TrackedInst.Index { - const key: TrackedInst = .{ - .file = file, - .inst = inst, - }; - const gop = try ip.tracked_insts.getOrPut(gpa, key); - return @enumFromInt(gop.index); + const full_hash = Hash.hash(0, std.mem.asBytes(&key)); + const hash: u32 = @truncate(full_hash >> 32); + const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; + var map = shard.shared.tracked_inst_map.acquire(); + const Map = @TypeOf(map); + var map_mask = map.header().mask(); + var map_index = hash; + while (true) : (map_index += 1) { + map_index &= map_mask; + const entry = &map.entries[map_index]; + const index = entry.acquire().unwrap() orelse break; + if (entry.hash != hash) continue; + if (std.meta.eql(index.resolveFull(ip), key)) return index; + } + shard.mutate.tracked_inst_map.mutex.lock(); + defer shard.mutate.tracked_inst_map.mutex.unlock(); + if (map.entries != shard.shared.tracked_inst_map.entries) { + shard.mutate.tracked_inst_map.len += 1; + map = shard.shared.tracked_inst_map; + map_mask = map.header().mask(); + map_index = hash; + } + while (true) : (map_index += 1) { + map_index &= map_mask; + const entry = &map.entries[map_index]; + const index = entry.acquire().unwrap() orelse break; + if (entry.hash != hash) continue; + if (std.meta.eql(index.resolveFull(ip), key)) return index; + } + defer shard.mutate.tracked_inst_map.len += 1; + const local = ip.getLocal(tid); + local.mutate.tracked_insts.mutex.lock(); + defer local.mutate.tracked_insts.mutex.unlock(); + const list = local.getMutableTrackedInsts(gpa); + try list.ensureUnusedCapacity(1); + const map_header = map.header().*; + if (shard.mutate.tracked_inst_map.len < map_header.capacity * 3 / 5) { + const entry = &map.entries[map_index]; + entry.hash = hash; + const index = (TrackedInst.Index.Unwrapped{ + .tid = tid, + .index = list.mutate.len, + }).wrap(ip); + list.appendAssumeCapacity(.{key}); + entry.release(index.toOptional()); + return index; + } + const arena_state = &local.mutate.arena; + var arena = arena_state.promote(gpa); + defer arena_state.* = arena.state; + const new_map_capacity = map_header.capacity * 2; + const new_map_buf = try arena.allocator().alignedAlloc( + u8, + Map.alignment, + Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry), + ); + const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) }; + new_map.header().* = .{ .capacity = new_map_capacity }; + @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined }); + const new_map_mask = new_map.header().mask(); + map_index = 0; + while (map_index < map_header.capacity) : (map_index += 1) { + const entry = &map.entries[map_index]; + const index = entry.value.unwrap() orelse continue; + const item_hash = entry.hash; + var new_map_index = item_hash; + while (true) : (new_map_index += 1) { + new_map_index &= new_map_mask; + const new_entry = &new_map.entries[new_map_index]; + if (new_entry.value != .none) continue; + new_entry.* = .{ + .value = index.toOptional(), + .hash = item_hash, + }; + break; + } + } + map = new_map; + map_index = hash; + while (true) : (map_index += 1) { + map_index &= new_map_mask; + if (map.entries[map_index].value == .none) break; + } + const index = (TrackedInst.Index.Unwrapped{ + .tid = tid, + .index = list.mutate.len, + }).wrap(ip); + list.appendAssumeCapacity(.{key}); + map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash }; + shard.shared.tracked_inst_map.release(new_map); + return index; } /// Analysis Unit. Represents a single entity which undergoes semantic analysis. @@ -337,9 +418,12 @@ const Local = struct { arena: std.heap.ArenaAllocator.State, items: ListMutate, - extra: ListMutate, + extra: MutexListMutate, limbs: ListMutate, strings: ListMutate, + tracked_insts: MutexListMutate, + files: ListMutate, + maps: ListMutate, decls: BucketListMutate, namespaces: BucketListMutate, @@ -350,6 +434,9 @@ const Local = struct { extra: Extra, limbs: Limbs, strings: Strings, + tracked_insts: TrackedInsts, + files: List(File), + maps: Maps, decls: Decls, namespaces: Namespaces, @@ -370,16 +457,18 @@ const Local = struct { else => @compileError("unsupported host"), }; const Strings = List(struct { u8 }); + const TrackedInsts = List(struct { TrackedInst }); + const Maps = List(struct { FieldMap }); const decls_bucket_width = 8; const decls_bucket_mask = (1 << decls_bucket_width) - 1; const decl_next_free_field = "src_namespace"; - const Decls = List(struct { *[1 << decls_bucket_width]Module.Decl }); + const Decls = List(struct { *[1 << decls_bucket_width]Zcu.Decl }); const namespaces_bucket_width = 8; const namespaces_bucket_mask = (1 << namespaces_bucket_width) - 1; const namespace_next_free_field = "decl_index"; - const Namespaces = List(struct { *[1 << namespaces_bucket_width]Module.Namespace }); + const Namespaces = List(struct { *[1 << namespaces_bucket_width]Zcu.Namespace }); const ListMutate = struct { len: u32, @@ -389,6 +478,16 @@ const Local = struct { }; }; + const MutexListMutate = struct { + mutex: std.Thread.Mutex, + list: ListMutate, + + const empty: MutexListMutate = .{ + .mutex = .{}, + .list = ListMutate.empty, + }; + }; + const BucketListMutate = struct { last_bucket_len: u32, buckets_list: ListMutate, @@ -410,7 +509,7 @@ const Local = struct { const ListSelf = @This(); const Mutable = struct { - gpa: std.mem.Allocator, + gpa: Allocator, arena: *std.heap.ArenaAllocator.State, mutate: *ListMutate, list: *ListSelf, @@ -435,14 +534,17 @@ const Local = struct { .is_tuple = elem_info.is_tuple, } }); } - fn SliceElem(comptime opts: struct { is_const: bool = false }) type { + fn PtrElem(comptime opts: struct { + size: std.builtin.Type.Pointer.Size, + is_const: bool = false, + }) type { const elem_info = @typeInfo(Elem).Struct; const elem_fields = elem_info.fields; var new_fields: [elem_fields.len]std.builtin.Type.StructField = undefined; for (&new_fields, elem_fields) |*new_field, elem_field| new_field.* = .{ .name = elem_field.name, .type = @Type(.{ .Pointer = .{ - .size = .Slice, + .size = opts.size, .is_const = opts.is_const, .is_volatile = false, .alignment = 0, @@ -463,6 +565,23 @@ const Local = struct { } }); } + pub fn addOne(mutable: Mutable) Allocator.Error!PtrElem(.{ .size = .One }) { + try mutable.ensureUnusedCapacity(1); + return mutable.addOneAssumeCapacity(); + } + + pub fn addOneAssumeCapacity(mutable: Mutable) PtrElem(.{ .size = .One }) { + const index = mutable.mutate.len; + assert(index < mutable.list.header().capacity); + mutable.mutate.len = index + 1; + const mutable_view = mutable.view().slice(); + var ptr: PtrElem(.{ .size = .One }) = undefined; + inline for (fields) |field| { + @field(ptr, @tagName(field)) = &mutable_view.items(field)[index]; + } + return ptr; + } + pub fn append(mutable: Mutable, elem: Elem) Allocator.Error!void { try mutable.ensureUnusedCapacity(1); mutable.appendAssumeCapacity(elem); @@ -476,14 +595,14 @@ const Local = struct { pub fn appendSliceAssumeCapacity( mutable: Mutable, - slice: SliceElem(.{ .is_const = true }), + slice: PtrElem(.{ .size = .Slice, .is_const = true }), ) void { if (fields.len == 0) return; const start = mutable.mutate.len; const slice_len = @field(slice, @tagName(fields[0])).len; assert(slice_len <= mutable.list.header().capacity - start); mutable.mutate.len = @intCast(start + slice_len); - const mutable_view = mutable.view(); + const mutable_view = mutable.view().slice(); inline for (fields) |field| { const field_slice = @field(slice, @tagName(field)); assert(field_slice.len == slice_len); @@ -500,7 +619,7 @@ const Local = struct { const start = mutable.mutate.len; assert(len <= mutable.list.header().capacity - start); mutable.mutate.len = @intCast(start + len); - const mutable_view = mutable.view(); + const mutable_view = mutable.view().slice(); inline for (fields) |field| { @memset(mutable_view.items(field)[start..][0..len], @field(elem, @tagName(field))); } @@ -515,7 +634,7 @@ const Local = struct { const start = mutable.mutate.len; assert(len <= mutable.list.header().capacity - start); mutable.mutate.len = @intCast(start + len); - const mutable_view = mutable.view(); + const mutable_view = mutable.view().slice(); var ptr_array: PtrArrayElem(len) = undefined; inline for (fields) |field| { @field(ptr_array, @tagName(field)) = mutable_view.items(field)[start..][0..len]; @@ -523,17 +642,17 @@ const Local = struct { return ptr_array; } - pub fn addManyAsSlice(mutable: Mutable, len: usize) Allocator.Error!SliceElem(.{}) { + pub fn addManyAsSlice(mutable: Mutable, len: usize) Allocator.Error!PtrElem(.{ .size = .Slice }) { try mutable.ensureUnusedCapacity(len); return mutable.addManyAsSliceAssumeCapacity(len); } - pub fn addManyAsSliceAssumeCapacity(mutable: Mutable, len: usize) SliceElem(.{}) { + pub fn addManyAsSliceAssumeCapacity(mutable: Mutable, len: usize) PtrElem(.{ .size = .Slice }) { const start = mutable.mutate.len; assert(len <= mutable.list.header().capacity - start); mutable.mutate.len = @intCast(start + len); - const mutable_view = mutable.view(); - var slice: SliceElem(.{}) = undefined; + const mutable_view = mutable.view().slice(); + var slice: PtrElem(.{ .size = .Slice }) = undefined; inline for (fields) |field| { @field(slice, @tagName(field)) = mutable_view.items(field)[start..][0..len]; } @@ -578,7 +697,7 @@ const Local = struct { mutable.list.release(new_list); } - fn view(mutable: Mutable) View { + pub fn view(mutable: Mutable) View { const capacity = mutable.list.header().capacity; assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` return .{ @@ -602,7 +721,7 @@ const Local = struct { const View = std.MultiArrayList(Elem); /// Must be called when accessing from another thread. - fn acquire(list: *const ListSelf) ListSelf { + pub fn acquire(list: *const ListSelf) ListSelf { return .{ .bytes = @atomicLoad([*]align(@alignOf(Elem)) u8, &list.bytes, .acquire) }; } fn release(list: *ListSelf, new_list: ListSelf) void { @@ -616,7 +735,7 @@ const Local = struct { return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset); } - fn view(list: ListSelf) View { + pub fn view(list: ListSelf) View { const capacity = list.header().capacity; assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` return .{ @@ -628,7 +747,7 @@ const Local = struct { }; } - pub fn getMutableItems(local: *Local, gpa: std.mem.Allocator) List(Item).Mutable { + pub fn getMutableItems(local: *Local, gpa: Allocator) List(Item).Mutable { return .{ .gpa = gpa, .arena = &local.mutate.arena, @@ -637,11 +756,11 @@ const Local = struct { }; } - pub fn getMutableExtra(local: *Local, gpa: std.mem.Allocator) Extra.Mutable { + pub fn getMutableExtra(local: *Local, gpa: Allocator) Extra.Mutable { return .{ .gpa = gpa, .arena = &local.mutate.arena, - .mutate = &local.mutate.extra, + .mutate = &local.mutate.extra.list, .list = &local.shared.extra, }; } @@ -650,7 +769,7 @@ const Local = struct { /// On 64-bit systems, this array is used for big integers and associated metadata. /// Use the helper methods instead of accessing this directly in order to not /// violate the above mechanism. - pub fn getMutableLimbs(local: *Local, gpa: std.mem.Allocator) Limbs.Mutable { + pub fn getMutableLimbs(local: *Local, gpa: Allocator) Limbs.Mutable { return switch (@sizeOf(Limb)) { @sizeOf(u32) => local.getMutableExtra(gpa), @sizeOf(u64) => .{ @@ -668,7 +787,7 @@ const Local = struct { /// is referencing the data here whether they want to store both index and length, /// thus allowing null bytes, or store only index, and use null-termination. The /// `strings` array is agnostic to either usage. - pub fn getMutableStrings(local: *Local, gpa: std.mem.Allocator) Strings.Mutable { + pub fn getMutableStrings(local: *Local, gpa: Allocator) Strings.Mutable { return .{ .gpa = gpa, .arena = &local.mutate.arena, @@ -677,6 +796,49 @@ const Local = struct { }; } + /// An index into `tracked_insts` gives a reference to a single ZIR instruction which + /// persists across incremental updates. + pub fn getMutableTrackedInsts(local: *Local, gpa: Allocator) TrackedInsts.Mutable { + return .{ + .gpa = gpa, + .arena = &local.mutate.arena, + .mutate = &local.mutate.tracked_insts.list, + .list = &local.shared.tracked_insts, + }; + } + + /// Elements are ordered identically to the `import_table` field of `Zcu`. + /// + /// Unlike `import_table`, this data is serialized as part of incremental + /// compilation state. + /// + /// Key is the hash of the path to this file, used to store + /// `InternPool.TrackedInst`. + /// + /// Value is the `Decl` of the struct that represents this `File`. + pub fn getMutableFiles(local: *Local, gpa: Allocator) List(File).Mutable { + return .{ + .gpa = gpa, + .arena = &local.mutate.arena, + .mutate = &local.mutate.files, + .list = &local.shared.files, + }; + } + + /// Some types such as enums, structs, and unions need to store mappings from field names + /// to field index, or value to field index. In such cases, they will store the underlying + /// field names and values directly, relying on one of these maps, stored separately, + /// to provide lookup. + /// These are not serialized; it is computed upon deserialization. + pub fn getMutableMaps(local: *Local, gpa: Allocator) Maps.Mutable { + return .{ + .gpa = gpa, + .arena = &local.mutate.arena, + .mutate = &local.mutate.maps, + .list = &local.shared.maps, + }; + } + /// Rather than allocating Decl objects with an Allocator, we instead allocate /// them with this BucketList. This provides four advantages: /// * Stable memory so that one thread can access a Decl object while another @@ -687,7 +849,7 @@ const Local = struct { /// serialization trivial. /// * It provides a unique integer to be used for anonymous symbol names, avoiding /// multi-threaded contention on an atomic counter. - pub fn getMutableDecls(local: *Local, gpa: std.mem.Allocator) Decls.Mutable { + pub fn getMutableDecls(local: *Local, gpa: Allocator) Decls.Mutable { return .{ .gpa = gpa, .arena = &local.mutate.arena, @@ -697,7 +859,7 @@ const Local = struct { } /// Same pattern as with `getMutableDecls`. - pub fn getMutableNamespaces(local: *Local, gpa: std.mem.Allocator) Namespaces.Mutable { + pub fn getMutableNamespaces(local: *Local, gpa: Allocator) Namespaces.Mutable { return .{ .gpa = gpa, .arena = &local.mutate.arena, @@ -719,11 +881,13 @@ const Shard = struct { shared: struct { map: Map(Index), string_map: Map(OptionalNullTerminatedString), + tracked_inst_map: Map(TrackedInst.Index.Optional), } align(std.atomic.cache_line), mutate: struct { // TODO: measure cost of sharing unrelated mutate state map: Mutate align(std.atomic.cache_line), string_map: Mutate align(std.atomic.cache_line), + tracked_inst_map: Mutate align(std.atomic.cache_line), }, const Mutate = struct { @@ -812,8 +976,6 @@ const Hash = std.hash.Wyhash; const InternPool = @This(); const Zcu = @import("Zcu.zig"); -/// Deprecated. -const Module = Zcu; const Zir = std.zig.Zir; /// An index into `maps` which might be `none`. @@ -831,9 +993,37 @@ pub const OptionalMapIndex = enum(u32) { pub const MapIndex = enum(u32) { _, + pub fn get(map_index: MapIndex, ip: *InternPool) *FieldMap { + const unwrapped_map_index = map_index.unwrap(ip); + const maps = ip.getLocalShared(unwrapped_map_index.tid).maps.acquire(); + return &maps.view().items(.@"0")[unwrapped_map_index.index]; + } + + pub fn getConst(map_index: MapIndex, ip: *const InternPool) FieldMap { + return map_index.get(@constCast(ip)).*; + } + pub fn toOptional(i: MapIndex) OptionalMapIndex { return @enumFromInt(@intFromEnum(i)); } + + const Unwrapped = struct { + tid: Zcu.PerThread.Id, + index: u32, + + fn wrap(unwrapped: Unwrapped, ip: *const InternPool) MapIndex { + assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); + assert(unwrapped.index <= ip.getIndexMask(u32)); + return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + unwrapped.index); + } + }; + fn unwrap(map_index: MapIndex, ip: *const InternPool) Unwrapped { + return .{ + .tid = @enumFromInt(@intFromEnum(map_index) >> ip.tid_shift_32 & ip.getTidMask()), + .index = @intFromEnum(map_index) & ip.getIndexMask(u32), + }; + } }; pub const RuntimeIndex = enum(u32) { @@ -938,6 +1128,34 @@ pub const OptionalNamespaceIndex = enum(u32) { } }; +pub const FileIndex = enum(u32) { + _, + + const Unwrapped = struct { + tid: Zcu.PerThread.Id, + index: u32, + + fn wrap(unwrapped: Unwrapped, ip: *const InternPool) FileIndex { + assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask()); + assert(unwrapped.index <= ip.getIndexMask(u32)); + return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 | + unwrapped.index); + } + }; + pub fn unwrap(file_index: FileIndex, ip: *const InternPool) Unwrapped { + return .{ + .tid = @enumFromInt(@intFromEnum(file_index) >> ip.tid_shift_32 & ip.getTidMask()), + .index = @intFromEnum(file_index) & ip.getIndexMask(u32), + }; + } +}; + +const File = struct { + bin_digest: Cache.BinDigest, + file: *Zcu.File, + root_decl: OptionalDeclIndex, +}; + /// An index into `strings`. pub const String = enum(u32) { /// An empty string. @@ -1240,7 +1458,7 @@ pub const Key = union(enum) { /// Look up field index based on field name. pub fn nameIndex(self: ErrorSetType, ip: *const InternPool, name: NullTerminatedString) ?u32 { - const map = &ip.maps.items[@intFromEnum(self.names_map.unwrap().?)]; + const map = self.names_map.unwrap().?.getConst(ip); const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) }; const field_index = map.getIndexAdapted(name, adapter) orelse return null; return @intCast(field_index); @@ -2665,7 +2883,7 @@ pub const LoadedStructType = struct { if (i >= self.field_types.len) return null; return i; }; - const map = &ip.maps.items[@intFromEnum(names_map)]; + const map = names_map.getConst(ip); const adapter: NullTerminatedString.Adapter = .{ .strings = self.field_names.get(ip) }; const field_index = map.getIndexAdapted(name, adapter) orelse return null; return @intCast(field_index); @@ -2783,20 +3001,25 @@ pub const LoadedStructType = struct { } pub fn setInitsWip(s: LoadedStructType, ip: *InternPool) bool { - switch (s.layout) { - .@"packed" => { - const flag = &s.packedFlagsPtr(ip).field_inits_wip; - if (flag.*) return true; - flag.* = true; - return false; - }, - .auto, .@"extern" => { - const flag = &s.flagsPtr(ip).field_inits_wip; - if (flag.*) return true; - flag.* = true; - return false; - }, - } + const local = ip.getLocal(s.tid); + local.mutate.extra.mutex.lock(); + defer local.mutate.extra.mutex.unlock(); + return switch (s.layout) { + .@"packed" => @as(Tag.TypeStructPacked.Flags, @bitCast(@atomicRmw( + u32, + @as(*u32, @ptrCast(s.packedFlagsPtr(ip))), + .Or, + @bitCast(Tag.TypeStructPacked.Flags{ .field_inits_wip = true }), + .acq_rel, + ))).field_inits_wip, + .auto, .@"extern" => @as(Tag.TypeStruct.Flags, @bitCast(@atomicRmw( + u32, + @as(*u32, @ptrCast(s.flagsPtr(ip))), + .Or, + @bitCast(Tag.TypeStruct.Flags{ .field_inits_wip = true }), + .acq_rel, + ))).field_inits_wip, + }; } pub fn clearInitsWip(s: LoadedStructType, ip: *InternPool) void { @@ -2962,6 +3185,7 @@ pub const LoadedStructType = struct { pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { const unwrapped_index = index.unwrap(ip); const extra_list = unwrapped_index.getExtra(ip); + const extra_items = extra_list.view().items(.@"0"); const item = unwrapped_index.getItem(ip); switch (item.tag) { .type_struct => { @@ -2982,10 +3206,12 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .names_map = .none, .captures = CaptureValue.Slice.empty, }; - const extra = extraDataTrail(extra_list, Tag.TypeStruct, item.data); - const fields_len = extra.data.fields_len; - var extra_index = extra.end; - const captures_len = if (extra.data.flags.any_captures) c: { + const decl: DeclIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "decl").?]); + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]); + const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?]; + const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .monotonic)); + var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStruct).Struct.fields.len); + const captures_len = if (flags.any_captures) c: { const len = extra_list.view().items(.@"0")[extra_index]; extra_index += 1; break :c len; @@ -2996,7 +3222,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .len = captures_len, }; extra_index += captures_len; - if (extra.data.flags.is_reified) { + if (flags.is_reified) { extra_index += 2; // PackedU64 } const field_types: Index.Slice = .{ @@ -3005,7 +3231,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .len = fields_len, }; extra_index += fields_len; - const names_map: OptionalMapIndex, const names = if (!extra.data.flags.is_tuple) n: { + const names_map: OptionalMapIndex, const names = if (!flags.is_tuple) n: { const names_map: OptionalMapIndex = @enumFromInt(extra_list.view().items(.@"0")[extra_index]); extra_index += 1; const names: NullTerminatedString.Slice = .{ @@ -3016,7 +3242,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { extra_index += fields_len; break :n .{ names_map, names }; } else .{ .none, NullTerminatedString.Slice.empty }; - const inits: Index.Slice = if (extra.data.flags.any_default_inits) i: { + const inits: Index.Slice = if (flags.any_default_inits) i: { const inits: Index.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, @@ -3025,12 +3251,12 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { extra_index += fields_len; break :i inits; } else Index.Slice.empty; - const namespace: OptionalNamespaceIndex = if (extra.data.flags.has_namespace) n: { + const namespace: OptionalNamespaceIndex = if (flags.has_namespace) n: { const n: NamespaceIndex = @enumFromInt(extra_list.view().items(.@"0")[extra_index]); extra_index += 1; break :n n.toOptional(); } else .none; - const aligns: Alignment.Slice = if (extra.data.flags.any_aligned_fields) a: { + const aligns: Alignment.Slice = if (flags.any_aligned_fields) a: { const a: Alignment.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, @@ -3039,7 +3265,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable; break :a a; } else Alignment.Slice.empty; - const comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) c: { + const comptime_bits: LoadedStructType.ComptimeBits = if (flags.any_comptime_fields) c: { const len = std.math.divCeil(u32, fields_len, 32) catch unreachable; const c: LoadedStructType.ComptimeBits = .{ .tid = unwrapped_index.tid, @@ -3049,7 +3275,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { extra_index += len; break :c c; } else LoadedStructType.ComptimeBits.empty; - const runtime_order: LoadedStructType.RuntimeOrder.Slice = if (!extra.data.flags.is_extern) ro: { + const runtime_order: LoadedStructType.RuntimeOrder.Slice = if (!flags.is_extern) ro: { const ro: LoadedStructType.RuntimeOrder.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, @@ -3070,10 +3296,10 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { return .{ .tid = unwrapped_index.tid, .extra_index = item.data, - .decl = extra.data.decl.toOptional(), + .decl = decl.toOptional(), .namespace = namespace, - .zir_index = extra.data.zir_index.toOptional(), - .layout = if (extra.data.flags.is_extern) .@"extern" else .auto, + .zir_index = zir_index.toOptional(), + .layout = if (flags.is_extern) .@"extern" else .auto, .field_names = names, .field_types = field_types, .field_inits = inits, @@ -3086,11 +3312,15 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { }; }, .type_struct_packed, .type_struct_packed_inits => { - const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, item.data); + const decl: DeclIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "decl").?]); + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]); + const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?]; + const namespace: OptionalNamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]); + const names_map: MapIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "names_map").?]); + const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .monotonic)); + var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStructPacked).Struct.fields.len); const has_inits = item.tag == .type_struct_packed_inits; - const fields_len = extra.data.fields_len; - var extra_index = extra.end; - const captures_len = if (extra.data.flags.any_captures) c: { + const captures_len = if (flags.any_captures) c: { const len = extra_list.view().items(.@"0")[extra_index]; extra_index += 1; break :c len; @@ -3101,7 +3331,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .len = captures_len, }; extra_index += captures_len; - if (extra.data.flags.is_reified) { + if (flags.is_reified) { extra_index += 2; // PackedU64 } const field_types: Index.Slice = .{ @@ -3128,9 +3358,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { return .{ .tid = unwrapped_index.tid, .extra_index = item.data, - .decl = extra.data.decl.toOptional(), - .namespace = extra.data.namespace, - .zir_index = extra.data.zir_index.toOptional(), + .decl = decl.toOptional(), + .namespace = namespace, + .zir_index = zir_index.toOptional(), .layout = .@"packed", .field_names = field_names, .field_types = field_types, @@ -3139,7 +3369,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .runtime_order = LoadedStructType.RuntimeOrder.Slice.empty, .comptime_bits = LoadedStructType.ComptimeBits.empty, .offsets = LoadedStructType.Offsets.empty, - .names_map = extra.data.names_map.toOptional(), + .names_map = names_map.toOptional(), .captures = captures, }; }, @@ -3183,7 +3413,7 @@ const LoadedEnumType = struct { /// Look up field index based on field name. pub fn nameIndex(self: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 { - const map = &ip.maps.items[@intFromEnum(self.names_map)]; + const map = self.names_map.getConst(ip); const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) }; const field_index = map.getIndexAdapted(name, adapter) orelse return null; return @intCast(field_index); @@ -3203,7 +3433,7 @@ const LoadedEnumType = struct { else => unreachable, }; if (self.values_map.unwrap()) |values_map| { - const map = &ip.maps.items[@intFromEnum(values_map)]; + const map = values_map.getConst(ip); const adapter: Index.Adapter = .{ .indexes = self.values.get(ip) }; const field_index = map.getIndexAdapted(int_tag_val, adapter) orelse return null; return @intCast(field_index); @@ -4476,11 +4706,11 @@ pub const Tag = enum(u8) { flags: Flags, pub const Flags = packed struct(u32) { - any_captures: bool, + any_captures: bool = false, /// Dependency loop detection when resolving field inits. - field_inits_wip: bool, - inits_resolved: bool, - is_reified: bool, + field_inits_wip: bool = false, + inits_resolved: bool = false, + is_reified: bool = false, _: u28 = 0, }; }; @@ -4526,36 +4756,36 @@ pub const Tag = enum(u8) { size: u32, pub const Flags = packed struct(u32) { - any_captures: bool, - is_extern: bool, - known_non_opv: bool, - requires_comptime: RequiresComptime, - is_tuple: bool, - assumed_runtime_bits: bool, - assumed_pointer_aligned: bool, - has_namespace: bool, - any_comptime_fields: bool, - any_default_inits: bool, - any_aligned_fields: bool, + any_captures: bool = false, + is_extern: bool = false, + known_non_opv: bool = false, + requires_comptime: RequiresComptime = @enumFromInt(0), + is_tuple: bool = false, + assumed_runtime_bits: bool = false, + assumed_pointer_aligned: bool = false, + has_namespace: bool = false, + any_comptime_fields: bool = false, + any_default_inits: bool = false, + any_aligned_fields: bool = false, /// `.none` until layout_resolved - alignment: Alignment, + alignment: Alignment = @enumFromInt(0), /// Dependency loop detection when resolving struct alignment. - alignment_wip: bool, + alignment_wip: bool = false, /// Dependency loop detection when resolving field types. - field_types_wip: bool, + field_types_wip: bool = false, /// Dependency loop detection when resolving struct layout. - layout_wip: bool, + layout_wip: bool = false, /// Indicates whether `size`, `alignment`, runtime field order, and /// field offets are populated. - layout_resolved: bool, + layout_resolved: bool = false, /// Dependency loop detection when resolving field inits. - field_inits_wip: bool, + field_inits_wip: bool = false, /// Indicates whether `field_inits` has been resolved. - inits_resolved: bool, - // The types and all its fields have had their layout resolved. Even through pointer, + inits_resolved: bool = false, + // The types and all its fields have had their layout resolved. Even through pointer = false, // which `layout_resolved` does not ensure. - fully_resolved: bool, - is_reified: bool, + fully_resolved: bool = false, + is_reified: bool = false, _: u6 = 0, }; }; @@ -4599,12 +4829,12 @@ pub const FuncAnalysis = packed struct(u32) { /// inline, which means no runtime version of the function will be generated. inline_only, in_progress, - /// There will be a corresponding ErrorMsg in Module.failed_decls + /// There will be a corresponding ErrorMsg in Zcu.failed_decls sema_failure, /// This function might be OK but it depends on another Decl which did not /// successfully complete semantic analysis. dependency_failure, - /// There will be a corresponding ErrorMsg in Module.failed_decls. + /// There will be a corresponding ErrorMsg in Zcu.failed_decls. /// Indicates that semantic analysis succeeded, but code generation for /// this function failed. codegen_failure, @@ -5201,6 +5431,9 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .extra = Local.Extra.empty, .limbs = Local.Limbs.empty, .strings = Local.Strings.empty, + .tracked_insts = Local.TrackedInsts.empty, + .files = Local.List(File).empty, + .maps = Local.Maps.empty, .decls = Local.Decls.empty, .namespaces = Local.Namespaces.empty, @@ -5209,9 +5442,12 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .arena = .{}, .items = Local.ListMutate.empty, - .extra = Local.ListMutate.empty, + .extra = Local.MutexListMutate.empty, .limbs = Local.ListMutate.empty, .strings = Local.ListMutate.empty, + .tracked_insts = Local.MutexListMutate.empty, + .files = Local.ListMutate.empty, + .maps = Local.ListMutate.empty, .decls = Local.BucketListMutate.empty, .namespaces = Local.BucketListMutate.empty, @@ -5226,10 +5462,12 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { .shared = .{ .map = Shard.Map(Index).empty, .string_map = Shard.Map(OptionalNullTerminatedString).empty, + .tracked_inst_map = Shard.Map(TrackedInst.Index.Optional).empty, }, .mutate = .{ .map = Shard.Mutate.empty, .string_map = Shard.Mutate.empty, + .tracked_inst_map = Shard.Mutate.empty, }, }); @@ -5267,11 +5505,6 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { } pub fn deinit(ip: *InternPool, gpa: Allocator) void { - for (ip.maps.items) |*map| map.deinit(gpa); - ip.maps.deinit(gpa); - - ip.tracked_insts.deinit(gpa); - ip.src_hash_deps.deinit(gpa); ip.decl_val_deps.deinit(gpa); ip.func_ies_deps.deinit(gpa); @@ -5283,8 +5516,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.dep_entries.deinit(gpa); ip.free_dep_entries.deinit(gpa); - ip.files.deinit(gpa); - gpa.free(ip.shards); for (ip.locals) |*local| { const buckets_len = local.mutate.namespaces.buckets_list.len; @@ -5301,6 +5532,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { namespace.usingnamespace_set.deinit(gpa); } }; + const maps = local.getMutableMaps(gpa); + if (maps.mutate.len > 0) for (maps.view().items(.@"0")) |*map| map.deinit(gpa); local.mutate.arena.promote(gpa).deinit(); } gpa.free(ip.locals); @@ -5400,40 +5633,46 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_struct => .{ .struct_type = ns: { if (data == 0) break :ns .empty_struct; const extra_list = unwrapped_index.getExtra(ip); - const extra = extraDataTrail(extra_list, Tag.TypeStruct, data); - if (extra.data.flags.is_reified) { - assert(!extra.data.flags.any_captures); + const extra_items = extra_list.view().items(.@"0"); + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]); + const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .monotonic)); + const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStruct).Struct.fields.len); + if (flags.is_reified) { + assert(!flags.any_captures); break :ns .{ .reified = .{ - .zir_index = extra.data.zir_index, - .type_hash = extraData(extra_list, PackedU64, extra.end).get(), + .zir_index = zir_index, + .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(), } }; } break :ns .{ .declared = .{ - .zir_index = extra.data.zir_index, - .captures = .{ .owned = if (extra.data.flags.any_captures) .{ + .zir_index = zir_index, + .captures = .{ .owned = if (flags.any_captures) .{ .tid = unwrapped_index.tid, - .start = extra.end + 1, - .len = extra_list.view().items(.@"0")[extra.end], + .start = end_extra_index + 1, + .len = extra_list.view().items(.@"0")[end_extra_index], } else CaptureValue.Slice.empty }, } }; } }, .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = ns: { const extra_list = unwrapped_index.getExtra(ip); - const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - if (extra.data.flags.is_reified) { - assert(!extra.data.flags.any_captures); + const extra_items = extra_list.view().items(.@"0"); + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]); + const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .monotonic)); + const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStructPacked).Struct.fields.len); + if (flags.is_reified) { + assert(!flags.any_captures); break :ns .{ .reified = .{ - .zir_index = extra.data.zir_index, - .type_hash = extraData(extra_list, PackedU64, extra.end).get(), + .zir_index = zir_index, + .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(), } }; } break :ns .{ .declared = .{ - .zir_index = extra.data.zir_index, - .captures = .{ .owned = if (extra.data.flags.any_captures) .{ + .zir_index = zir_index, + .captures = .{ .owned = if (flags.any_captures) .{ .tid = unwrapped_index.tid, - .start = extra.end + 1, - .len = extra_list.view().items(.@"0")[extra.end], + .start = end_extra_index + 1, + .len = extra_items[end_extra_index], } else CaptureValue.Slice.empty }, } }; } }, @@ -5914,27 +6153,32 @@ fn extraFuncDecl(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke } fn extraFuncInstance(ip: *const InternPool, tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.Func { - const P = Tag.FuncInstance; - const fi = extraDataTrail(extra, P, extra_index); - const func_decl = ip.funcDeclInfo(fi.data.generic_owner); + const extra_items = extra.view().items(.@"0"); + const analysis_extra_index = extra_index + std.meta.fieldIndex(Tag.FuncInstance, "analysis").?; + const analysis: FuncAnalysis = @bitCast(@atomicLoad(u32, &extra_items[analysis_extra_index], .monotonic)); + const owner_decl: DeclIndex = @enumFromInt(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "owner_decl").?]); + const ty: Index = @enumFromInt(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "ty").?]); + const generic_owner: Index = @enumFromInt(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "generic_owner").?]); + const func_decl = ip.funcDeclInfo(generic_owner); + const end_extra_index = extra_index + @as(u32, @typeInfo(Tag.FuncInstance).Struct.fields.len); return .{ .tid = tid, - .ty = fi.data.ty, - .uncoerced_ty = fi.data.ty, - .analysis_extra_index = extra_index + std.meta.fieldIndex(P, "analysis").?, + .ty = ty, + .uncoerced_ty = ty, + .analysis_extra_index = analysis_extra_index, .zir_body_inst_extra_index = func_decl.zir_body_inst_extra_index, - .resolved_error_set_extra_index = if (fi.data.analysis.inferred_error_set) fi.end else 0, - .branch_quota_extra_index = extra_index + std.meta.fieldIndex(P, "branch_quota").?, - .owner_decl = fi.data.owner_decl, + .resolved_error_set_extra_index = if (analysis.inferred_error_set) end_extra_index else 0, + .branch_quota_extra_index = extra_index + std.meta.fieldIndex(Tag.FuncInstance, "branch_quota").?, + .owner_decl = owner_decl, .zir_body_inst = func_decl.zir_body_inst, .lbrace_line = func_decl.lbrace_line, .rbrace_line = func_decl.rbrace_line, .lbrace_column = func_decl.lbrace_column, .rbrace_column = func_decl.rbrace_column, - .generic_owner = fi.data.generic_owner, + .generic_owner = generic_owner, .comptime_args = .{ .tid = tid, - .start = fi.end + @intFromBool(fi.data.analysis.inferred_error_set), + .start = end_extra_index + @intFromBool(analysis.inferred_error_set), .len = ip.funcTypeParamsLen(func_decl.ty), }, }; @@ -6206,8 +6450,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All assert(error_set_type.names_map == .none); assert(std.sort.isSorted(NullTerminatedString, error_set_type.names.get(ip), {}, NullTerminatedString.indexLessThan)); const names = error_set_type.names.get(ip); - const names_map = try ip.addMap(gpa, names.len); - addStringsToMap(ip, names_map, names); + const names_map = try ip.addMap(gpa, tid, names.len); + ip.addStringsToMap(names_map, names); const names_len = error_set_type.names.len; try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).Struct.fields.len + names_len); items.appendAssumeCapacity(.{ @@ -7107,8 +7351,8 @@ pub fn getStructType( const items = local.getMutableItems(gpa); const extra = local.getMutableExtra(gpa); - const names_map = try ip.addMap(gpa, ini.fields_len); - errdefer _ = ip.maps.pop(); + const names_map = try ip.addMap(gpa, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; const zir_index = switch (ini.key) { inline else => |x| x.zir_index, @@ -7655,17 +7899,18 @@ pub fn getErrorSetType( const extra = local.getMutableExtra(gpa); try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).Struct.fields.len + names.len); + const names_map = try ip.addMap(gpa, tid, names.len); + errdefer local.mutate.maps.len -= 1; + // The strategy here is to add the type unconditionally, then to ask if it // already exists, and if so, revert the lengths of the mutated arrays. // This is similar to what `getOrPutTrailingString` does. const prev_extra_len = extra.mutate.len; errdefer extra.mutate.len = prev_extra_len; - const predicted_names_map: MapIndex = @enumFromInt(ip.maps.items.len); - const error_set_extra_index = addExtraAssumeCapacity(extra, Tag.ErrorSet{ .names_len = @intCast(names.len), - .names_map = predicted_names_map, + .names_map = names_map, }); extra.appendSliceAssumeCapacity(.{@ptrCast(names)}); errdefer extra.mutate.len = prev_extra_len; @@ -7685,11 +7930,7 @@ pub fn getErrorSetType( }); errdefer items.mutate.len -= 1; - const names_map = try ip.addMap(gpa, names.len); - assert(names_map == predicted_names_map); - errdefer _ = ip.maps.pop(); - - addStringsToMap(ip, names_map, names); + ip.addStringsToMap(names_map, names); return gop.put(); } @@ -7955,6 +8196,7 @@ fn finishFuncInstance( const fn_owner_decl = ip.declPtr(ip.funcDeclOwner(generic_owner)); const decl_index = try ip.createDecl(gpa, tid, .{ .name = undefined, + .fqn = undefined, .src_namespace = fn_owner_decl.src_namespace, .has_tv = true, .owns_tv = true, @@ -7980,6 +8222,8 @@ fn finishFuncInstance( decl.name = try ip.getOrPutStringFmt(gpa, tid, "{}__anon_{d}", .{ fn_owner_decl.name.fmt(ip), @intFromEnum(decl_index), }, .no_embedded_nulls); + decl.fqn = try ip.namespacePtr(fn_owner_decl.src_namespace) + .internFullyQualifiedName(ip, gpa, tid, decl.name); } pub const EnumTypeInit = struct { @@ -8052,7 +8296,7 @@ pub const WipEnumType = struct { return null; } assert(ip.typeOf(value) == @as(Index, @enumFromInt(extra_items[wip.tag_ty_index]))); - const map = &ip.maps.items[@intFromEnum(wip.values_map.unwrap().?)]; + const map = wip.values_map.unwrap().?.get(ip); const field_index = map.count(); const indexes = extra_items[wip.values_start..][0..field_index]; const adapter: Index.Adapter = .{ .indexes = @ptrCast(indexes) }; @@ -8098,8 +8342,8 @@ pub fn getEnumType( try items.ensureUnusedCapacity(1); const extra = local.getMutableExtra(gpa); - const names_map = try ip.addMap(gpa, ini.fields_len); - errdefer _ = ip.maps.pop(); + const names_map = try ip.addMap(gpa, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; switch (ini.tag_mode) { .auto => { @@ -8152,11 +8396,11 @@ pub fn getEnumType( }, .explicit, .nonexhaustive => { const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: { - const values_map = try ip.addMap(gpa, ini.fields_len); + const values_map = try ip.addMap(gpa, tid, ini.fields_len); break :m values_map.toOptional(); }; errdefer if (ini.has_values) { - _ = ip.maps.pop(); + local.mutate.maps.len -= 1; }; try extra.ensureUnusedCapacity(@typeInfo(EnumExplicit).Struct.fields.len + @@ -8245,8 +8489,8 @@ pub fn getGeneratedTagEnumType( try items.ensureUnusedCapacity(1); const extra = local.getMutableExtra(gpa); - const names_map = try ip.addMap(gpa, ini.names.len); - errdefer _ = ip.maps.pop(); + const names_map = try ip.addMap(gpa, tid, ini.names.len); + errdefer local.mutate.maps.len -= 1; ip.addStringsToMap(names_map, ini.names); const fields_len: u32 = @intCast(ini.names.len); @@ -8279,8 +8523,8 @@ pub fn getGeneratedTagEnumType( ini.values.len); // field values const values_map: OptionalMapIndex = if (ini.values.len != 0) m: { - const map = try ip.addMap(gpa, ini.values.len); - addIndexesToMap(ip, map, ini.values); + const map = try ip.addMap(gpa, tid, ini.values.len); + ip.addIndexesToMap(map, ini.values); break :m map.toOptional(); } else .none; // We don't clean up the values map on error! @@ -8311,7 +8555,9 @@ pub fn getGeneratedTagEnumType( errdefer extra.mutate.len = prev_extra_len; errdefer switch (ini.tag_mode) { .auto => {}, - .explicit, .nonexhaustive => _ = if (ini.values.len != 0) ip.maps.pop(), + .explicit, .nonexhaustive => if (ini.values.len != 0) { + local.mutate.maps.len -= 1; + }, }; var gop = try ip.getOrPutKey(gpa, tid, .{ .enum_type = .{ @@ -8415,7 +8661,7 @@ fn addStringsToMap( map_index: MapIndex, strings: []const NullTerminatedString, ) void { - const map = &ip.maps.items[@intFromEnum(map_index)]; + const map = map_index.get(ip); const adapter: NullTerminatedString.Adapter = .{ .strings = strings }; for (strings) |string| { const gop = map.getOrPutAssumeCapacityAdapted(string, adapter); @@ -8428,7 +8674,7 @@ fn addIndexesToMap( map_index: MapIndex, indexes: []const Index, ) void { - const map = &ip.maps.items[@intFromEnum(map_index)]; + const map = map_index.get(ip); const adapter: Index.Adapter = .{ .indexes = indexes }; for (indexes) |index| { const gop = map.getOrPutAssumeCapacityAdapted(index, adapter); @@ -8436,12 +8682,14 @@ fn addIndexesToMap( } } -fn addMap(ip: *InternPool, gpa: Allocator, cap: usize) Allocator.Error!MapIndex { - const ptr = try ip.maps.addOne(gpa); - errdefer _ = ip.maps.pop(); - ptr.* = .{}; - try ptr.ensureTotalCapacity(gpa, cap); - return @enumFromInt(ip.maps.items.len - 1); +fn addMap(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, cap: usize) Allocator.Error!MapIndex { + const maps = ip.getLocal(tid).getMutableMaps(gpa); + const unwrapped: MapIndex.Unwrapped = .{ .tid = tid, .index = maps.mutate.len }; + const ptr = try maps.addOne(); + errdefer maps.mutate.len = unwrapped.index; + ptr[0].* = .{}; + try ptr[0].ensureTotalCapacity(gpa, cap); + return unwrapped.wrap(ip); } /// This operation only happens under compile error conditions. @@ -9167,10 +9415,13 @@ pub fn errorUnionPayload(ip: *const InternPool, ty: Index) Index { /// The is only legal because the initializer is not part of the hash. pub fn mutateVarInit(ip: *InternPool, index: Index, init_index: Index) void { const unwrapped_index = index.unwrap(ip); - const extra_list = unwrapped_index.getExtra(ip); + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lock(); + defer local.mutate.extra.mutex.unlock(); + const extra_items = local.shared.extra.view().items(.@"0"); const item = unwrapped_index.getItem(ip); assert(item.tag == .variable); - @atomicStore(u32, &extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(Tag.Variable, "init").?], @intFromEnum(init_index), .release); + @atomicStore(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.Variable, "init").?], @intFromEnum(init_index), .release); } pub fn dump(ip: *const InternPool) void { @@ -9185,14 +9436,14 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { var decls_len: usize = 0; for (ip.locals) |*local| { items_len += local.mutate.items.len; - extra_len += local.mutate.extra.len; + extra_len += local.mutate.extra.list.len; limbs_len += local.mutate.limbs.len; decls_len += local.mutate.decls.buckets_list.len; } const items_size = (1 + 4) * items_len; const extra_size = 4 * extra_len; const limbs_size = 8 * limbs_len; - const decls_size = @sizeOf(Module.Decl) * decls_len; + const decls_size = @sizeOf(Zcu.Decl) * decls_len; // TODO: map overhead size is not taken into account const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + decls_size; @@ -9619,29 +9870,22 @@ pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator) try bw.flush(); } -pub fn declPtr(ip: *InternPool, decl_index: DeclIndex) *Module.Decl { +pub fn declPtr(ip: *InternPool, decl_index: DeclIndex) *Zcu.Decl { return @constCast(ip.declPtrConst(decl_index)); } -pub fn declPtrConst(ip: *const InternPool, decl_index: DeclIndex) *const Module.Decl { +pub fn declPtrConst(ip: *const InternPool, decl_index: DeclIndex) *const Zcu.Decl { const unwrapped_decl_index = decl_index.unwrap(ip); const decls = ip.getLocalShared(unwrapped_decl_index.tid).decls.acquire(); const decls_bucket = decls.view().items(.@"0")[unwrapped_decl_index.bucket_index]; return &decls_bucket[unwrapped_decl_index.index]; } -pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Module.Namespace { - const unwrapped_namespace_index = namespace_index.unwrap(ip); - const namespaces = ip.getLocalShared(unwrapped_namespace_index.tid).namespaces.acquire(); - const namespaces_bucket = namespaces.view().items(.@"0")[unwrapped_namespace_index.bucket_index]; - return &namespaces_bucket[unwrapped_namespace_index.index]; -} - pub fn createDecl( ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, - initialization: Module.Decl, + initialization: Zcu.Decl, ) Allocator.Error!DeclIndex { const local = ip.getLocal(tid); const free_list_next = local.mutate.decls.free_list; @@ -9658,7 +9902,7 @@ pub fn createDecl( var arena = decls.arena.promote(decls.gpa); defer decls.arena.* = arena.state; decls.appendAssumeCapacity(.{try arena.allocator().create( - [1 << Local.decls_bucket_width]Module.Decl, + [1 << Local.decls_bucket_width]Zcu.Decl, )}); } const unwrapped_decl_index: DeclIndex.Unwrapped = .{ @@ -9681,11 +9925,18 @@ pub fn destroyDecl(ip: *InternPool, tid: Zcu.PerThread.Id, decl_index: DeclIndex local.mutate.decls.free_list = @intFromEnum(decl_index); } +pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Zcu.Namespace { + const unwrapped_namespace_index = namespace_index.unwrap(ip); + const namespaces = ip.getLocalShared(unwrapped_namespace_index.tid).namespaces.acquire(); + const namespaces_bucket = namespaces.view().items(.@"0")[unwrapped_namespace_index.bucket_index]; + return &namespaces_bucket[unwrapped_namespace_index.index]; +} + pub fn createNamespace( ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, - initialization: Module.Namespace, + initialization: Zcu.Namespace, ) Allocator.Error!NamespaceIndex { const local = ip.getLocal(tid); const free_list_next = local.mutate.namespaces.free_list; @@ -9703,7 +9954,7 @@ pub fn createNamespace( var arena = namespaces.arena.promote(namespaces.gpa); defer namespaces.arena.* = arena.state; namespaces.appendAssumeCapacity(.{try arena.allocator().create( - [1 << Local.namespaces_bucket_width]Module.Namespace, + [1 << Local.namespaces_bucket_width]Zcu.Namespace, )}); } const unwrapped_namespace_index: NamespaceIndex.Unwrapped = .{ @@ -9735,6 +9986,27 @@ pub fn destroyNamespace( local.mutate.namespaces.free_list = @intFromEnum(namespace_index); } +pub fn filePtr(ip: *InternPool, file_index: FileIndex) *Zcu.File { + const file_index_unwrapped = file_index.unwrap(ip); + const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); + return files.view().items(.file)[file_index_unwrapped.index]; +} + +pub fn createFile( + ip: *InternPool, + gpa: Allocator, + tid: Zcu.PerThread.Id, + file: File, +) Allocator.Error!FileIndex { + const files = ip.getLocal(tid).getMutableFiles(gpa); + const file_index_unwrapped: FileIndex.Unwrapped = .{ + .tid = tid, + .index = files.mutate.len, + }; + try files.append(file); + return file_index_unwrapped.wrap(ip); +} + const EmbeddedNulls = enum { no_embedded_nulls, maybe_embedded_nulls, @@ -9813,7 +10085,7 @@ pub fn getOrPutTrailingString( } const key: []const u8 = strings.view().items(.@"0")[start..]; const value: embedded_nulls.StringType() = - @enumFromInt(@as(u32, @intFromEnum(tid)) << ip.tid_shift_32 | start); + @enumFromInt(@intFromEnum((String.Unwrapped{ .tid = tid, .index = start }).wrap(ip))); const has_embedded_null = std.mem.indexOfScalar(u8, key, 0) != null; switch (embedded_nulls) { .no_embedded_nulls => assert(!has_embedded_null), @@ -9859,10 +10131,10 @@ pub fn getOrPutTrailingString( defer shard.mutate.string_map.len += 1; const map_header = map.header().*; if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) { + strings.appendAssumeCapacity(.{0}); const entry = &map.entries[map_index]; entry.hash = hash; entry.release(@enumFromInt(@intFromEnum(value))); - strings.appendAssumeCapacity(.{0}); return value; } const arena_state = &ip.getLocal(tid).mutate.arena; @@ -9901,12 +10173,12 @@ pub fn getOrPutTrailingString( map_index &= new_map_mask; if (map.entries[map_index].value == .none) break; } + strings.appendAssumeCapacity(.{0}); map.entries[map_index] = .{ .value = @enumFromInt(@intFromEnum(value)), .hash = hash, }; shard.shared.string_map.release(new_map); - strings.appendAssumeCapacity(.{0}); return value; } @@ -10654,7 +10926,7 @@ pub fn addFieldName( name: NullTerminatedString, ) ?u32 { const extra_items = extra.view().items(.@"0"); - const map = &ip.maps.items[@intFromEnum(names_map)]; + const map = names_map.get(ip); const field_index = map.count(); const strings = extra_items[names_start..][0..field_index]; const adapter: NullTerminatedString.Adapter = .{ .strings = @ptrCast(strings) }; @@ -10672,3 +10944,160 @@ fn ptrsHaveSameAlignment(ip: *InternPool, a_ty: Index, a_info: Key.PtrType, b_ty return a_info.flags.alignment == b_info.flags.alignment and (a_info.child == b_info.child or a_info.flags.alignment != .none); } + +const GlobalErrorSet = struct { + shared: struct { + names: Names, + map: Shard.Map(GlobalErrorSet.Index), + } align(std.atomic.cache_line), + mutate: Local.MutexListMutate align(std.atomic.cache_line), + + const Names = Local.List(struct { NullTerminatedString }); + + const empty: GlobalErrorSet = .{ + .shared = .{ + .names = Names.empty, + .map = Shard.Map(GlobalErrorSet.Index).empty, + }, + .mutate = Local.MutexListMutate.empty, + }; + + const Index = enum(Zcu.ErrorInt) { + none = 0, + _, + }; + + /// Not thread-safe, may only be called from the main thread. + pub fn getNamesFromMainThread(ges: *const GlobalErrorSet) []const NullTerminatedString { + const len = ges.mutate.list.len; + return if (len > 0) ges.shared.names.view().items(.@"0")[0..len] else &.{}; + } + + fn getErrorValue( + ges: *GlobalErrorSet, + gpa: Allocator, + arena_state: *std.heap.ArenaAllocator.State, + name: NullTerminatedString, + ) Allocator.Error!GlobalErrorSet.Index { + if (name == .empty) return .none; + const hash = std.hash.uint32(@intFromEnum(name)); + var map = ges.shared.map.acquire(); + const Map = @TypeOf(map); + var map_mask = map.header().mask(); + const names = ges.shared.names.acquire(); + var map_index = hash; + while (true) : (map_index += 1) { + map_index &= map_mask; + const entry = &map.entries[map_index]; + const index = entry.acquire(); + if (index == .none) break; + if (entry.hash != hash) continue; + if (names.view().items(.@"0")[@intFromEnum(index) - 1] == name) return index; + } + ges.mutate.mutex.lock(); + defer ges.mutate.mutex.unlock(); + if (map.entries != ges.shared.map.entries) { + map = ges.shared.map; + map_mask = map.header().mask(); + map_index = hash; + } + while (true) : (map_index += 1) { + map_index &= map_mask; + const entry = &map.entries[map_index]; + const index = entry.value; + if (index == .none) break; + if (entry.hash != hash) continue; + if (names.view().items(.@"0")[@intFromEnum(index) - 1] == name) return index; + } + const mutable_names: Names.Mutable = .{ + .gpa = gpa, + .arena = arena_state, + .mutate = &ges.mutate.list, + .list = &ges.shared.names, + }; + try mutable_names.ensureUnusedCapacity(1); + const map_header = map.header().*; + if (ges.mutate.list.len < map_header.capacity * 3 / 5) { + mutable_names.appendAssumeCapacity(.{name}); + const index: GlobalErrorSet.Index = @enumFromInt(mutable_names.mutate.len); + const entry = &map.entries[map_index]; + entry.hash = hash; + entry.release(index); + return index; + } + var arena = arena_state.promote(gpa); + defer arena_state.* = arena.state; + const new_map_capacity = map_header.capacity * 2; + const new_map_buf = try arena.allocator().alignedAlloc( + u8, + Map.alignment, + Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry), + ); + const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) }; + new_map.header().* = .{ .capacity = new_map_capacity }; + @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined }); + const new_map_mask = new_map.header().mask(); + map_index = 0; + while (map_index < map_header.capacity) : (map_index += 1) { + const entry = &map.entries[map_index]; + const index = entry.value; + if (index == .none) continue; + const item_hash = entry.hash; + var new_map_index = item_hash; + while (true) : (new_map_index += 1) { + new_map_index &= new_map_mask; + const new_entry = &new_map.entries[new_map_index]; + if (new_entry.value != .none) continue; + new_entry.* = .{ + .value = index, + .hash = item_hash, + }; + break; + } + } + map = new_map; + map_index = hash; + while (true) : (map_index += 1) { + map_index &= new_map_mask; + if (map.entries[map_index].value == .none) break; + } + mutable_names.appendAssumeCapacity(.{name}); + const index: GlobalErrorSet.Index = @enumFromInt(mutable_names.mutate.len); + map.entries[map_index] = .{ .value = index, .hash = hash }; + ges.shared.map.release(new_map); + return index; + } + + fn getErrorValueIfExists( + ges: *const GlobalErrorSet, + name: NullTerminatedString, + ) ?GlobalErrorSet.Index { + if (name == .empty) return .none; + const hash = std.hash.uint32(@intFromEnum(name)); + const map = ges.shared.map.acquire(); + const map_mask = map.header().mask(); + const names_items = ges.shared.names.acquire().view().items(.@"0"); + var map_index = hash; + while (true) : (map_index += 1) { + map_index &= map_mask; + const entry = &map.entries[map_index]; + const index = entry.acquire(); + if (index == .none) return null; + if (entry.hash != hash) continue; + if (names_items[@intFromEnum(index) - 1] == name) return index; + } + } +}; + +pub fn getErrorValue( + ip: *InternPool, + gpa: Allocator, + tid: Zcu.PerThread.Id, + name: NullTerminatedString, +) Allocator.Error!Zcu.ErrorInt { + return @intFromEnum(try ip.global_error_set.getErrorValue(gpa, &ip.getLocal(tid).mutate.arena, name)); +} + +pub fn getErrorValueIfExists(ip: *const InternPool, name: NullTerminatedString) ?Zcu.ErrorInt { + return @intFromEnum(ip.global_error_set.getErrorValueIfExists(name) orelse return null); +} diff --git a/src/Sema.zig b/src/Sema.zig index b897228f58..1062ece2be 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -835,12 +835,11 @@ pub const Block = struct { } fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index { - const sema = block.sema; - const gpa = sema.gpa; - const zcu = sema.pt.zcu; - const ip = &zcu.intern_pool; - const file_index = block.getFileScopeIndex(zcu); - return ip.trackZir(gpa, file_index, inst); + const pt = block.sema.pt; + return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ + .file = block.getFileScopeIndex(pt.zcu), + .inst = inst, + }); } }; @@ -2878,7 +2877,7 @@ fn createAnonymousDeclTypeNamed( switch (name_strategy) { .anon => {}, // handled after switch .parent => { - try zcu.initNewAnonDecl(new_decl_index, val, block.type_name_ctx); + try pt.initNewAnonDecl(new_decl_index, val, block.type_name_ctx, .none); return new_decl_index; }, .func => func_strat: { @@ -2923,7 +2922,7 @@ fn createAnonymousDeclTypeNamed( try writer.writeByte(')'); const name = try ip.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls); - try zcu.initNewAnonDecl(new_decl_index, val, name); + try pt.initNewAnonDecl(new_decl_index, val, name, .none); return new_decl_index; }, .dbg_var => { @@ -2937,7 +2936,7 @@ fn createAnonymousDeclTypeNamed( const name = try ip.getOrPutStringFmt(gpa, pt.tid, "{}.{s}", .{ block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), }, .no_embedded_nulls); - try zcu.initNewAnonDecl(new_decl_index, val, name); + try pt.initNewAnonDecl(new_decl_index, val, name, .none); return new_decl_index; }, else => {}, @@ -2958,7 +2957,7 @@ fn createAnonymousDeclTypeNamed( const name = ip.getOrPutStringFmt(gpa, pt.tid, "{}__{s}_{d}", .{ block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(new_decl_index), }, .no_embedded_nulls) catch unreachable; - try zcu.initNewAnonDecl(new_decl_index, val, name); + try pt.initNewAnonDecl(new_decl_index, val, name, .none); return new_decl_index; } @@ -3474,7 +3473,7 @@ fn zirErrorSetDecl( const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); const name = sema.code.nullTerminatedString(name_index); const name_ip = try mod.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); - _ = try mod.getErrorValue(name_ip); + _ = try pt.getErrorValue(name_ip); const result = names.getOrPutAssumeCapacity(name_ip); assert(!result.found_existing); // verified in AstGen } @@ -5527,13 +5526,12 @@ fn failWithBadStructFieldAccess( const zcu = pt.zcu; const ip = &zcu.intern_pool; const decl = zcu.declPtr(struct_type.decl.unwrap().?); - const fqn = try decl.fullyQualifiedName(pt); const msg = msg: { const msg = try sema.errMsg( field_src, "no field named '{}' in struct '{}'", - .{ field_name.fmt(ip), fqn.fmt(ip) }, + .{ field_name.fmt(ip), decl.fqn.fmt(ip) }, ); errdefer msg.destroy(sema.gpa); try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{}); @@ -5554,15 +5552,13 @@ fn failWithBadUnionFieldAccess( const zcu = pt.zcu; const ip = &zcu.intern_pool; const gpa = sema.gpa; - const decl = zcu.declPtr(union_obj.decl); - const fqn = try decl.fullyQualifiedName(pt); const msg = msg: { const msg = try sema.errMsg( field_src, "no field named '{}' in union '{}'", - .{ field_name.fmt(ip), fqn.fmt(ip) }, + .{ field_name.fmt(ip), decl.fqn.fmt(ip) }, ); errdefer msg.destroy(gpa); try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{}); @@ -6059,7 +6055,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr else => |e| return e, }; - const result = zcu.importPkg(c_import_mod) catch |err| + const result = pt.importPkg(c_import_mod) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); const path_digest = zcu.filePathDigest(result.file_index); @@ -6721,13 +6717,7 @@ fn addDbgVar( if (block.need_debug_scope) |ptr| ptr.* = true; // Add the name to the AIR. - const name_extra_index: u32 = @intCast(sema.air_extra.items.len); - const elements_used = name.len / 4 + 1; - try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used); - const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); - @memcpy(buffer[0..name.len], name); - buffer[name.len] = 0; - sema.air_extra.items.len += elements_used; + const name_extra_index = try sema.appendAirString(name); _ = try block.addInst(.{ .tag = air_tag, @@ -6738,6 +6728,16 @@ fn addDbgVar( }); } +pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!u32 { + const str_extra_index: u32 = @intCast(sema.air_extra.items.len); + const elements_used = str.len / 4 + 1; + const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used); + const buffer = mem.sliceAsBytes(elements); + @memcpy(buffer[0..str.len], str); + buffer[str.len] = 0; + return str_extra_index; +} + fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; const mod = pt.zcu; @@ -8357,13 +8357,6 @@ fn instantiateGenericCall( } } else { // The parameter is runtime-known. - child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{ - .tag = .arg, - .data = .{ .arg = .{ - .ty = Air.internedToRef(arg_ty.toIntern()), - .src_index = @intCast(arg_index), - } }, - })); const param_name: Zir.NullTerminatedString = switch (param_tag) { .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.start, .param => name: { @@ -8373,6 +8366,16 @@ fn instantiateGenericCall( }, else => unreachable, }; + child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{ + .tag = .arg, + .data = .{ .arg = .{ + .ty = Air.internedToRef(arg_ty.toIntern()), + .name = if (child_block.ownerModule().strip) + .none + else + @enumFromInt(try sema.appendAirString(fn_zir.nullTerminatedString(param_name))), + } }, + })); try child_block.params.append(sema.arena, .{ .ty = arg_ty.toIntern(), // This is the type after coercion .is_comptime = false, // We're adding only runtime args to the instantiation @@ -8702,7 +8705,7 @@ fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! inst_data.get(sema.code), .no_embedded_nulls, ); - _ = try pt.zcu.getErrorValue(name); + _ = try pt.getErrorValue(name); // Create an error set type with only this error value, and return the value. const error_set_type = try pt.singleErrorSetType(name); return Air.internedToRef((try pt.intern(.{ .err = .{ @@ -8732,7 +8735,7 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const err_name = ip.indexToKey(val.toIntern()).err.name; return Air.internedToRef((try pt.intValue( err_int_ty, - try mod.getErrorValue(err_name), + try pt.getErrorValue(err_name), )).toIntern()); } @@ -8743,10 +8746,7 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const names = ip.indexToKey(err_set_ty_index).error_set_type.names; switch (names.len) { 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()), - 1 => { - const int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[0]).?); - return pt.intRef(err_int_ty, int); - }, + 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?), else => {}, } }, @@ -8762,6 +8762,7 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const pt = sema.pt; const mod = pt.zcu; + const ip = &mod.intern_pool; const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.nodeOffset(extra.node); const operand_src = block.builtinCallArgSrc(extra.node, 0); @@ -8771,11 +8772,16 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt)); - if (int > mod.global_error_set.count() or int == 0) + if (int > len: { + const mutate = &ip.global_error_set.mutate; + mutate.mutex.lock(); + defer mutate.mutex.unlock(); + break :len mutate.list.len; + } or int == 0) return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); return Air.internedToRef((try pt.intern(.{ .err = .{ .ty = .anyerror_type, - .name = mod.global_error_set.keys()[int], + .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1], } }))); } try sema.requireRuntimeBlock(block, src, operand_src); @@ -13943,7 +13949,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operand_src = block.tokenOffset(inst_data.src_tok); const operand = inst_data.get(sema.code); - const result = zcu.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) { + const result = pt.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) { error.ImportOutsideModulePath => { return sema.fail(block, operand_src, "import of file outside module path: '{s}'", .{operand}); }, @@ -14002,7 +14008,7 @@ fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.R inst_data.get(sema.code), .no_embedded_nulls, ); - _ = try mod.getErrorValue(name); + _ = try pt.getErrorValue(name); const error_set_type = try pt.singleErrorSetType(name); return Air.internedToRef((try pt.intern(.{ .err = .{ .ty = error_set_type.toIntern(), @@ -19561,7 +19567,7 @@ fn zirRetErrValue( inst_data.get(sema.code), .no_embedded_nulls, ); - _ = try mod.getErrorValue(err_name); + _ = try pt.getErrorValue(err_name); // Return the error code from the function. const error_set_type = try pt.singleErrorSetType(err_name); const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{ @@ -21604,7 +21610,7 @@ fn zirReify( const name = try sema.sliceToIpString(block, src, name_val, .{ .needed_comptime_reason = "error set contents must be comptime-known", }); - _ = try mod.getErrorValue(name); + _ = try pt.getErrorValue(name); const gop = names.getOrPutAssumeCapacity(name); if (gop.found_existing) { return sema.fail(block, src, "duplicate error '{}'", .{ @@ -26500,7 +26506,7 @@ fn zirBuiltinExtern( const new_decl_index = try pt.allocateNewDecl(sema.owner_decl.src_namespace); errdefer pt.destroyDecl(new_decl_index); const new_decl = mod.declPtr(new_decl_index); - try mod.initNewAnonDecl( + try pt.initNewAnonDecl( new_decl_index, Value.fromInterned( if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn) @@ -26522,6 +26528,7 @@ fn zirBuiltinExtern( } }), ), options.name, + .none, ); new_decl.owns_tv = true; // Note that this will queue the anon decl for codegen, so that the backend can @@ -27481,7 +27488,7 @@ fn fieldVal( }, .simple_type => |t| { assert(t == .anyerror); - _ = try mod.getErrorValue(field_name); + _ = try pt.getErrorValue(field_name); }, else => unreachable, } @@ -27721,7 +27728,7 @@ fn fieldPtr( }, .simple_type => |t| { assert(t == .anyerror); - _ = try mod.getErrorValue(field_name); + _ = try pt.getErrorValue(field_name); }, else => unreachable, } @@ -36735,24 +36742,23 @@ fn generateUnionTagTypeNumbered( const new_decl_index = try pt.allocateNewDecl(block.namespace); errdefer pt.destroyDecl(new_decl_index); - const fqn = try union_owner_decl.fullyQualifiedName(pt); const name = try ip.getOrPutStringFmt( gpa, pt.tid, "@typeInfo({}).Union.tag_type.?", - .{fqn.fmt(ip)}, + .{union_owner_decl.fqn.fmt(ip)}, .no_embedded_nulls, ); - try mod.initNewAnonDecl( + try pt.initNewAnonDecl( new_decl_index, Value.@"unreachable", name, + name.toOptional(), ); errdefer pt.abortAnonDecl(new_decl_index); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - new_decl.name_fully_qualified = true; const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{ .decl = new_decl_index, @@ -36784,22 +36790,21 @@ fn generateUnionTagTypeSimple( const gpa = sema.gpa; const new_decl_index = new_decl_index: { - const fqn = try union_owner_decl.fullyQualifiedName(pt); const new_decl_index = try pt.allocateNewDecl(block.namespace); errdefer pt.destroyDecl(new_decl_index); const name = try ip.getOrPutStringFmt( gpa, pt.tid, "@typeInfo({}).Union.tag_type.?", - .{fqn.fmt(ip)}, + .{union_owner_decl.fqn.fmt(ip)}, .no_embedded_nulls, ); - try mod.initNewAnonDecl( + try pt.initNewAnonDecl( new_decl_index, Value.@"unreachable", name, + name.toOptional(), ); - mod.declPtr(new_decl_index).name_fully_qualified = true; break :new_decl_index new_decl_index; }; errdefer pt.abortAnonDecl(new_decl_index); diff --git a/src/Type.zig b/src/Type.zig index 57ac2310d5..65176e9a80 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -268,10 +268,10 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error return; }, .inferred_error_set_type => |func_index| { - try writer.writeAll("@typeInfo(@typeInfo(@TypeOf("); const owner_decl = mod.funcOwnerDeclPtr(func_index); - try owner_decl.renderFullyQualifiedName(mod, writer); - try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set"); + try writer.print("@typeInfo(@typeInfo(@TypeOf({})).Fn.return_type.?).ErrorUnion.error_set", .{ + owner_decl.fqn.fmt(ip), + }); }, .error_set_type => |error_set_type| { const names = error_set_type.names; @@ -334,10 +334,10 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error const struct_type = ip.loadStructType(ty.toIntern()); if (struct_type.decl.unwrap()) |decl_index| { const decl = mod.declPtr(decl_index); - try decl.renderFullyQualifiedName(mod, writer); + try writer.print("{}", .{decl.fqn.fmt(ip)}); } else if (ip.loadStructType(ty.toIntern()).namespace.unwrap()) |namespace_index| { const namespace = mod.namespacePtr(namespace_index); - try namespace.renderFullyQualifiedName(mod, .empty, writer); + try namespace.renderFullyQualifiedName(ip, .empty, writer); } else { try writer.writeAll("@TypeOf(.{})"); } @@ -367,15 +367,15 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error .union_type => { const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl); - try decl.renderFullyQualifiedName(mod, writer); + try writer.print("{}", .{decl.fqn.fmt(ip)}); }, .opaque_type => { const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl); - try decl.renderFullyQualifiedName(mod, writer); + try writer.print("{}", .{decl.fqn.fmt(ip)}); }, .enum_type => { const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl); - try decl.renderFullyQualifiedName(mod, writer); + try writer.print("{}", .{decl.fqn.fmt(ip)}); }, .func_type => |fn_info| { if (fn_info.is_noinline) { @@ -3451,7 +3451,7 @@ pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index { }; } -pub fn typeDeclSrcLine(ty: Type, zcu: *const Zcu) ?u32 { +pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { const ip = &zcu.intern_pool; const tracked = switch (ip.indexToKey(ty.toIntern())) { .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { diff --git a/src/Value.zig b/src/Value.zig index f114a2c7fa..da1151139f 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -417,7 +417,7 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro var bigint_buffer: BigIntSpace = undefined; const bigint = BigIntMutable.init( &bigint_buffer.limbs, - mod.global_error_set.getIndex(name).?, + ip.getErrorValueIfExists(name).?, ).toConst(); bigint.writeTwosComplement(buffer[0..byte_count], endian); }, @@ -427,7 +427,7 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro if (val.unionTag(mod)) |union_tag| { const union_obj = mod.typeToUnion(ty).?; const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?; - const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]); + const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); const field_val = try val.fieldValue(pt, field_index); const byte_count: usize = @intCast(field_type.abiSize(pt)); return writeToMemory(field_val, field_type, pt, buffer[0..byte_count]); @@ -1455,9 +1455,9 @@ pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTermi }; } -pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt { - return if (getErrorName(val, mod).unwrap()) |err_name| - @intCast(mod.global_error_set.getIndex(err_name).?) +pub fn getErrorInt(val: Value, zcu: *Zcu) Module.ErrorInt { + return if (getErrorName(val, zcu).unwrap()) |err_name| + zcu.intern_pool.getErrorValueIfExists(err_name).? else 0; } diff --git a/src/Zcu.zig b/src/Zcu.zig index 92a0765ebb..a9d80b4fdf 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -102,7 +102,7 @@ multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { /// `Compilation.update` of the process for a given `Compilation`. /// /// Indexes correspond 1:1 to `files`. -import_table: std.StringArrayHashMapUnmanaged(*File) = .{}, +import_table: std.StringArrayHashMapUnmanaged(File.Index) = .{}, /// The set of all the files which have been loaded with `@embedFile` in the Module. /// We keep track of this in order to iterate over it and check which files have been @@ -141,9 +141,6 @@ failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .{}, /// are stored here. cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .{}, -/// Key is the error name, index is the error tag value. Index 0 has a length-0 string. -global_error_set: GlobalErrorSet = .{}, - /// Maximum amount of distinct error values, set by --error-limit error_limit: ErrorInt, @@ -326,7 +323,10 @@ pub const Reference = struct { }; pub const Decl = struct { + /// Equal to `fqn` if already fully qualified. name: InternPool.NullTerminatedString, + /// Fully qualified name. + fqn: InternPool.NullTerminatedString, /// The most recent Value of the Decl after a successful semantic analysis. /// Populated when `has_tv`. val: Value, @@ -384,8 +384,6 @@ pub const Decl = struct { is_pub: bool, /// Whether the corresponding AST decl has a `export` keyword. is_exported: bool, - /// If true `name` is already fully qualified. - name_fully_qualified: bool = false, /// What kind of a declaration is this. kind: Kind, @@ -408,25 +406,6 @@ pub const Decl = struct { return extra.data.getBodies(@intCast(extra.end), zir); } - pub fn renderFullyQualifiedName(decl: Decl, zcu: *Zcu, writer: anytype) !void { - if (decl.name_fully_qualified) { - try writer.print("{}", .{decl.name.fmt(&zcu.intern_pool)}); - } else { - try zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedName(zcu, decl.name, writer); - } - } - - pub fn renderFullyQualifiedDebugName(decl: Decl, zcu: *Zcu, writer: anytype) !void { - return zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(zcu, decl.name, writer); - } - - pub fn fullyQualifiedName(decl: Decl, pt: Zcu.PerThread) !InternPool.NullTerminatedString { - return if (decl.name_fully_qualified) - decl.name - else - pt.zcu.namespacePtr(decl.src_namespace).fullyQualifiedName(pt, decl.name); - } - pub fn typeOf(decl: Decl, zcu: *const Zcu) Type { assert(decl.has_tv); return decl.val.typeOf(zcu); @@ -646,23 +625,27 @@ pub const Namespace = struct { return zcu.fileByIndex(ns.file_scope); } + pub fn fileScopeIp(ns: Namespace, ip: *InternPool) *File { + return ip.filePtr(ns.file_scope); + } + // This renders e.g. "std.fs.Dir.OpenOptions" pub fn renderFullyQualifiedName( ns: Namespace, - zcu: *Zcu, + ip: *InternPool, name: InternPool.NullTerminatedString, writer: anytype, ) @TypeOf(writer).Error!void { if (ns.parent.unwrap()) |parent| { - try zcu.namespacePtr(parent).renderFullyQualifiedName( - zcu, - zcu.declPtr(ns.decl_index).name, + try ip.namespacePtr(parent).renderFullyQualifiedName( + ip, + ip.declPtr(ns.decl_index).name, writer, ); } else { - try ns.fileScope(zcu).renderFullyQualifiedName(writer); + try ns.fileScopeIp(ip).renderFullyQualifiedName(writer); } - if (name != .empty) try writer.print(".{}", .{name.fmt(&zcu.intern_pool)}); + if (name != .empty) try writer.print(".{}", .{name.fmt(ip)}); } /// This renders e.g. "std/fs.zig:Dir.OpenOptions" @@ -686,46 +669,45 @@ pub const Namespace = struct { if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) }); } - pub fn fullyQualifiedName( + pub fn internFullyQualifiedName( ns: Namespace, - pt: Zcu.PerThread, + ip: *InternPool, + gpa: Allocator, + tid: Zcu.PerThread.Id, name: InternPool.NullTerminatedString, ) !InternPool.NullTerminatedString { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - const gpa = zcu.gpa; - const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); + const strings = ip.getLocal(tid).getMutableStrings(gpa); // Protects reads of interned strings from being reallocated during the call to // renderFullyQualifiedName. const slice = try strings.addManyAsSlice(count: { var count: usize = name.length(ip) + 1; var cur_ns = &ns; while (true) { - const decl = zcu.declPtr(cur_ns.decl_index); - cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse { - count += ns.fileScope(zcu).fullyQualifiedNameLen(); + const decl = ip.declPtr(cur_ns.decl_index); + cur_ns = ip.namespacePtr(cur_ns.parent.unwrap() orelse { + count += ns.fileScopeIp(ip).fullyQualifiedNameLen(); break :count count; }); count += decl.name.length(ip) + 1; } }); var fbs = std.io.fixedBufferStream(slice[0]); - ns.renderFullyQualifiedName(zcu, name, fbs.writer()) catch unreachable; + ns.renderFullyQualifiedName(ip, name, fbs.writer()) catch unreachable; assert(fbs.pos == slice[0].len); // Sanitize the name for nvptx which is more restrictive. // TODO This should be handled by the backend, not the frontend. Have a // look at how the C backend does it for inspiration. - const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; - if (cpu_arch.isNvptx()) { - for (slice[0]) |*byte| switch (byte.*) { - '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_', - else => {}, - }; - } + // FIXME This has bitrotted and is no longer able to be implemented here. + //const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; + //if (cpu_arch.isNvptx()) { + // for (slice[0]) |*byte| switch (byte.*) { + // '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_', + // else => {}, + // }; + //} - return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(slice[0].len), .no_embedded_nulls); + return ip.getOrPutTrailingString(gpa, tid, @intCast(slice[0].len), .no_embedded_nulls); } pub fn getType(ns: Namespace, zcu: *Zcu) Type { @@ -882,7 +864,7 @@ pub const File = struct { }; } - pub fn fullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString { + pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString { const gpa = pt.zcu.gpa; const ip = &pt.zcu.intern_pool; const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); @@ -910,7 +892,7 @@ pub const File = struct { } /// Add a reference to this file during AstGen. - pub fn addReference(file: *File, zcu: Zcu, ref: File.Reference) !void { + pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void { // Don't add the same module root twice. Note that since we always add module roots at the // front of the references array (see below), this loop is actually O(1) on valid code. if (ref == .root) { @@ -942,7 +924,7 @@ pub const File = struct { /// Mark this file and every file referenced by it as multi_pkg and report an /// astgen_failure error for them. AstGen must have completed in its entirety. - pub fn recursiveMarkMultiPkg(file: *File, mod: *Module) void { + pub fn recursiveMarkMultiPkg(file: *File, pt: Zcu.PerThread) void { file.multi_pkg = true; file.status = .astgen_failure; @@ -962,9 +944,9 @@ pub const File = struct { const import_path = file.zir.nullTerminatedString(item.data.name); if (mem.eql(u8, import_path, "builtin")) continue; - const res = mod.importFile(file, import_path) catch continue; + const res = pt.importFile(file, import_path) catch continue; if (!res.is_pkg and !res.file.multi_pkg) { - res.file.recursiveMarkMultiPkg(mod); + res.file.recursiveMarkMultiPkg(pt); } } } @@ -1033,6 +1015,14 @@ pub const ErrorMsg = struct { } }; +pub const AstGenSrc = union(enum) { + root, + import: struct { + importing_file: Zcu.File.Index, + import_tok: std.zig.Ast.TokenIndex, + }, +}; + /// Canonical reference to a position within a source file. pub const SrcLoc = struct { file_scope: *File, @@ -2406,7 +2396,6 @@ pub const CompileError = error{ pub fn init(mod: *Module, thread_count: usize) !void { const gpa = mod.gpa; try mod.intern_pool.init(gpa, thread_count); - try mod.global_error_set.put(gpa, .empty, {}); } pub fn deinit(zcu: *Zcu) void { @@ -2421,8 +2410,7 @@ pub fn deinit(zcu: *Zcu) void { for (zcu.import_table.keys()) |key| { gpa.free(key); } - for (0..zcu.import_table.entries.len) |file_index_usize| { - const file_index: File.Index = @enumFromInt(file_index_usize); + for (zcu.import_table.values()) |file_index| { pt.destroyFile(file_index); } zcu.import_table.deinit(gpa); @@ -2479,8 +2467,6 @@ pub fn deinit(zcu: *Zcu) void { zcu.single_exports.deinit(gpa); zcu.multi_exports.deinit(gpa); - zcu.global_error_set.deinit(gpa); - zcu.potentially_outdated.deinit(gpa); zcu.outdated.deinit(gpa); zcu.outdated_ready.deinit(gpa); @@ -3020,183 +3006,7 @@ pub const ImportFileResult = struct { is_pkg: bool, }; -pub fn importPkg(zcu: *Zcu, mod: *Package.Module) !ImportFileResult { - const gpa = zcu.gpa; - - // The resolved path is used as the key in the import table, to detect if - // an import refers to the same as another, despite different relative paths - // or differently mapped package names. - const resolved_path = try std.fs.path.resolve(gpa, &.{ - mod.root.root_dir.path orelse ".", - mod.root.sub_path, - mod.root_src_path, - }); - var keep_resolved_path = false; - defer if (!keep_resolved_path) gpa.free(resolved_path); - - const gop = try zcu.import_table.getOrPut(gpa, resolved_path); - errdefer _ = zcu.import_table.pop(); - if (gop.found_existing) { - try gop.value_ptr.*.addReference(zcu.*, .{ .root = mod }); - return .{ - .file = gop.value_ptr.*, - .file_index = @enumFromInt(gop.index), - .is_new = false, - .is_pkg = true, - }; - } - - const ip = &zcu.intern_pool; - - try ip.files.ensureUnusedCapacity(gpa, 1); - - if (mod.builtin_file) |builtin_file| { - keep_resolved_path = true; // It's now owned by import_table. - gop.value_ptr.* = builtin_file; - try builtin_file.addReference(zcu.*, .{ .root = mod }); - const path_digest = computePathDigest(zcu, mod, builtin_file.sub_file_path); - ip.files.putAssumeCapacityNoClobber(path_digest, .none); - return .{ - .file = builtin_file, - .file_index = @enumFromInt(ip.files.entries.len - 1), - .is_new = false, - .is_pkg = true, - }; - } - - const sub_file_path = try gpa.dupe(u8, mod.root_src_path); - errdefer gpa.free(sub_file_path); - - const new_file = try gpa.create(File); - errdefer gpa.destroy(new_file); - - keep_resolved_path = true; // It's now owned by import_table. - gop.value_ptr.* = new_file; - new_file.* = .{ - .sub_file_path = sub_file_path, - .source = undefined, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, - .stat = undefined, - .tree = undefined, - .zir = undefined, - .status = .never_loaded, - .mod = mod, - }; - - const path_digest = computePathDigest(zcu, mod, sub_file_path); - - try new_file.addReference(zcu.*, .{ .root = mod }); - ip.files.putAssumeCapacityNoClobber(path_digest, .none); - return .{ - .file = new_file, - .file_index = @enumFromInt(ip.files.entries.len - 1), - .is_new = true, - .is_pkg = true, - }; -} - -/// Called from a worker thread during AstGen. -/// Also called from Sema during semantic analysis. -pub fn importFile( - zcu: *Zcu, - cur_file: *File, - import_string: []const u8, -) !ImportFileResult { - const mod = cur_file.mod; - - if (std.mem.eql(u8, import_string, "std")) { - return zcu.importPkg(zcu.std_mod); - } - if (std.mem.eql(u8, import_string, "root")) { - return zcu.importPkg(zcu.root_mod); - } - if (mod.deps.get(import_string)) |pkg| { - return zcu.importPkg(pkg); - } - if (!mem.endsWith(u8, import_string, ".zig")) { - return error.ModuleNotFound; - } - const gpa = zcu.gpa; - - // The resolved path is used as the key in the import table, to detect if - // an import refers to the same as another, despite different relative paths - // or differently mapped package names. - const resolved_path = try std.fs.path.resolve(gpa, &.{ - mod.root.root_dir.path orelse ".", - mod.root.sub_path, - cur_file.sub_file_path, - "..", - import_string, - }); - - var keep_resolved_path = false; - defer if (!keep_resolved_path) gpa.free(resolved_path); - - const gop = try zcu.import_table.getOrPut(gpa, resolved_path); - errdefer _ = zcu.import_table.pop(); - if (gop.found_existing) return .{ - .file = gop.value_ptr.*, - .file_index = @enumFromInt(gop.index), - .is_new = false, - .is_pkg = false, - }; - - const ip = &zcu.intern_pool; - - try ip.files.ensureUnusedCapacity(gpa, 1); - - const new_file = try gpa.create(File); - errdefer gpa.destroy(new_file); - - const resolved_root_path = try std.fs.path.resolve(gpa, &.{ - mod.root.root_dir.path orelse ".", - mod.root.sub_path, - }); - defer gpa.free(resolved_root_path); - - const sub_file_path = p: { - const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path); - errdefer gpa.free(relative); - - if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) { - break :p relative; - } - return error.ImportOutsideModulePath; - }; - errdefer gpa.free(sub_file_path); - - log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{ - resolved_root_path, resolved_path, sub_file_path, import_string, - }); - - keep_resolved_path = true; // It's now owned by import_table. - gop.value_ptr.* = new_file; - new_file.* = .{ - .sub_file_path = sub_file_path, - .source = undefined, - .source_loaded = false, - .tree_loaded = false, - .zir_loaded = false, - .stat = undefined, - .tree = undefined, - .zir = undefined, - .status = .never_loaded, - .mod = mod, - }; - - const path_digest = computePathDigest(zcu, mod, sub_file_path); - ip.files.putAssumeCapacityNoClobber(path_digest, .none); - return .{ - .file = new_file, - .file_index = @enumFromInt(ip.files.entries.len - 1), - .is_new = true, - .is_pkg = false, - }; -} - -fn computePathDigest(zcu: *Zcu, mod: *Package.Module, sub_file_path: []const u8) Cache.BinDigest { +pub fn computePathDigest(zcu: *Zcu, mod: *Package.Module, sub_file_path: []const u8) Cache.BinDigest { const want_local_cache = mod == zcu.main_mod; var path_hash: Cache.HashHelper = .{}; path_hash.addBytes(build_options.version); @@ -3292,43 +3102,11 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit gop.value_ptr.* = @intCast(ref_idx); } -pub fn getErrorValue( - mod: *Module, - name: InternPool.NullTerminatedString, -) Allocator.Error!ErrorInt { - const gop = try mod.global_error_set.getOrPut(mod.gpa, name); - return @as(ErrorInt, @intCast(gop.index)); -} - -pub fn getErrorValueFromSlice( - mod: *Module, - name: []const u8, -) Allocator.Error!ErrorInt { - const interned_name = try mod.intern_pool.getOrPutString(mod.gpa, name); - return getErrorValue(mod, interned_name); -} - pub fn errorSetBits(mod: *Module) u16 { if (mod.error_limit == 0) return 0; return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error } -pub fn initNewAnonDecl( - mod: *Module, - new_decl_index: Decl.Index, - val: Value, - name: InternPool.NullTerminatedString, -) Allocator.Error!void { - const new_decl = mod.declPtr(new_decl_index); - - new_decl.name = name; - new_decl.val = val; - new_decl.alignment = .none; - new_decl.@"linksection" = .none; - new_decl.has_tv = true; - new_decl.analysis = .complete; -} - pub fn errNote( mod: *Module, src_loc: LazySrcLoc, @@ -3394,41 +3172,6 @@ pub fn handleUpdateExports( }; } -pub fn reportRetryableFileError( - zcu: *Zcu, - file_index: File.Index, - comptime format: []const u8, - args: anytype, -) error{OutOfMemory}!void { - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - - const file = zcu.fileByIndex(file_index); - file.status = .retryable_failure; - - const err_msg = try ErrorMsg.create( - gpa, - .{ - .base_node_inst = try ip.trackZir(gpa, file_index, .main_struct_inst), - .offset = .entire_file, - }, - format, - args, - ); - errdefer err_msg.destroy(gpa); - - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - - const gop = try zcu.failed_files.getOrPut(gpa, file); - if (gop.found_existing) { - if (gop.value_ptr.*) |old_err_msg| { - old_err_msg.destroy(gpa); - } - } - gop.value_ptr.* = err_msg; -} - pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index); if (gop.found_existing) { @@ -3744,22 +3487,28 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, Resolved return result; } -pub fn fileByIndex(zcu: *const Zcu, i: File.Index) *File { - return zcu.import_table.values()[@intFromEnum(i)]; +pub fn fileByIndex(zcu: *Zcu, file_index: File.Index) *File { + return zcu.intern_pool.filePtr(file_index); } /// Returns the `Decl` of the struct that represents this `File`. -pub fn fileRootDecl(zcu: *const Zcu, i: File.Index) Decl.OptionalIndex { +pub fn fileRootDecl(zcu: *const Zcu, file_index: File.Index) Decl.OptionalIndex { const ip = &zcu.intern_pool; - return ip.files.values()[@intFromEnum(i)]; + const file_index_unwrapped = file_index.unwrap(ip); + const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); + return files.view().items(.root_decl)[file_index_unwrapped.index]; } -pub fn setFileRootDecl(zcu: *Zcu, i: File.Index, root_decl: Decl.OptionalIndex) void { +pub fn setFileRootDecl(zcu: *Zcu, file_index: File.Index, root_decl: Decl.OptionalIndex) void { const ip = &zcu.intern_pool; - ip.files.values()[@intFromEnum(i)] = root_decl; + const file_index_unwrapped = file_index.unwrap(ip); + const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); + files.view().items(.root_decl)[file_index_unwrapped.index] = root_decl; } -pub fn filePathDigest(zcu: *const Zcu, i: File.Index) Cache.BinDigest { +pub fn filePathDigest(zcu: *const Zcu, file_index: File.Index) Cache.BinDigest { const ip = &zcu.intern_pool; - return ip.files.keys()[@intFromEnum(i)]; + const file_index_unwrapped = file_index.unwrap(ip); + const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); + return files.view().items(.bin_digest)[file_index_unwrapped.index]; } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index f638ffc538..4a1f257ddf 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -342,6 +342,7 @@ pub fn astGenFile( /// the Compilation mutex when acting on shared state. fn updateZirRefs(pt: Zcu.PerThread, file: *Zcu.File, file_index: Zcu.File.Index, old_zir: Zir) !void { const zcu = pt.zcu; + const ip = &zcu.intern_pool; const gpa = zcu.gpa; const new_zir = file.zir; @@ -355,109 +356,117 @@ fn updateZirRefs(pt: Zcu.PerThread, file: *Zcu.File, file_index: Zcu.File.Index, // TODO: this should be done after all AstGen workers complete, to avoid // iterating over this full set for every updated file. - for (zcu.intern_pool.tracked_insts.keys(), 0..) |*ti, idx_raw| { - const ti_idx: InternPool.TrackedInst.Index = @enumFromInt(idx_raw); - if (ti.file != file_index) continue; - const old_inst = ti.inst; - ti.inst = inst_map.get(ti.inst) orelse { - // Tracking failed for this instruction. Invalidate associated `src_hash` deps. - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - log.debug("tracking failed for %{d}", .{old_inst}); - try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); - continue; - }; + for (ip.locals, 0..) |*local, tid| { + local.mutate.tracked_insts.mutex.lock(); + defer local.mutate.tracked_insts.mutex.unlock(); + const tracked_insts_list = local.getMutableTrackedInsts(gpa); + for (tracked_insts_list.view().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| { + if (tracked_inst.file != file_index) continue; + const old_inst = tracked_inst.inst; + const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{ + .tid = @enumFromInt(tid), + .index = @intCast(tracked_inst_unwrapped_index), + }).wrap(ip); + tracked_inst.inst = inst_map.get(old_inst) orelse { + // Tracking failed for this instruction. Invalidate associated `src_hash` deps. + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + log.debug("tracking failed for %{d}", .{old_inst}); + try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); + continue; + }; - if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { - if (new_zir.getAssociatedSrcHash(ti.inst)) |new_hash| { - if (std.zig.srcHashEql(old_hash, new_hash)) { - break :hash_changed; + if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { + if (new_zir.getAssociatedSrcHash(tracked_inst.inst)) |new_hash| { + if (std.zig.srcHashEql(old_hash, new_hash)) { + break :hash_changed; + } + log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ + old_inst, + tracked_inst.inst, + std.fmt.fmtSliceHexLower(&old_hash), + std.fmt.fmtSliceHexLower(&new_hash), + }); } - log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ - old_inst, - ti.inst, - std.fmt.fmtSliceHexLower(&old_hash), - std.fmt.fmtSliceHexLower(&new_hash), - }); + // The source hash associated with this instruction changed - invalidate relevant dependencies. + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); } - // The source hash associated with this instruction changed - invalidate relevant dependencies. - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); - } - // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. - const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) { - .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) { - .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, + // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. + const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) { + .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) { + .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, + else => false, + }, else => false, - }, - else => false, - }; - if (!has_namespace) continue; + }; + if (!has_namespace) continue; - var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; - defer old_names.deinit(zcu.gpa); - { - var it = old_zir.declIterator(old_inst); - while (it.next()) |decl_inst| { - const decl_name = old_zir.getDeclaration(decl_inst)[0].name; - switch (decl_name) { - .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, - _ => if (decl_name.isNamedTest(old_zir)) continue, + var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; + defer old_names.deinit(zcu.gpa); + { + var it = old_zir.declIterator(old_inst); + while (it.next()) |decl_inst| { + const decl_name = old_zir.getDeclaration(decl_inst)[0].name; + switch (decl_name) { + .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, + _ => if (decl_name.isNamedTest(old_zir)) continue, + } + const name_zir = decl_name.toString(old_zir).?; + const name_ip = try zcu.intern_pool.getOrPutString( + zcu.gpa, + pt.tid, + old_zir.nullTerminatedString(name_zir), + .no_embedded_nulls, + ); + try old_names.put(zcu.gpa, name_ip, {}); } - const name_zir = decl_name.toString(old_zir).?; - const name_ip = try zcu.intern_pool.getOrPutString( - zcu.gpa, - pt.tid, - old_zir.nullTerminatedString(name_zir), - .no_embedded_nulls, - ); - try old_names.put(zcu.gpa, name_ip, {}); } - } - var any_change = false; - { - var it = new_zir.declIterator(ti.inst); - while (it.next()) |decl_inst| { - const decl_name = old_zir.getDeclaration(decl_inst)[0].name; - switch (decl_name) { - .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, - _ => if (decl_name.isNamedTest(old_zir)) continue, + var any_change = false; + { + var it = new_zir.declIterator(tracked_inst.inst); + while (it.next()) |decl_inst| { + const decl_name = old_zir.getDeclaration(decl_inst)[0].name; + switch (decl_name) { + .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, + _ => if (decl_name.isNamedTest(old_zir)) continue, + } + const name_zir = decl_name.toString(old_zir).?; + const name_ip = try zcu.intern_pool.getOrPutString( + zcu.gpa, + pt.tid, + old_zir.nullTerminatedString(name_zir), + .no_embedded_nulls, + ); + if (!old_names.swapRemove(name_ip)) continue; + // Name added + any_change = true; + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + try zcu.markDependeeOutdated(.{ .namespace_name = .{ + .namespace = tracked_inst_index, + .name = name_ip, + } }); } - const name_zir = decl_name.toString(old_zir).?; - const name_ip = try zcu.intern_pool.getOrPutString( - zcu.gpa, - pt.tid, - old_zir.nullTerminatedString(name_zir), - .no_embedded_nulls, - ); - if (!old_names.swapRemove(name_ip)) continue; - // Name added + } + // The only elements remaining in `old_names` now are any names which were removed. + for (old_names.keys()) |name_ip| { any_change = true; zcu.comp.mutex.lock(); defer zcu.comp.mutex.unlock(); try zcu.markDependeeOutdated(.{ .namespace_name = .{ - .namespace = ti_idx, + .namespace = tracked_inst_index, .name = name_ip, } }); } - } - // The only elements remaining in `old_names` now are any names which were removed. - for (old_names.keys()) |name_ip| { - any_change = true; - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - try zcu.markDependeeOutdated(.{ .namespace_name = .{ - .namespace = ti_idx, - .name = name_ip, - } }); - } - if (any_change) { - zcu.comp.mutex.lock(); - defer zcu.comp.mutex.unlock(); - try zcu.markDependeeOutdated(.{ .namespace = ti_idx }); + if (any_change) { + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index }); + } } } } @@ -548,7 +557,7 @@ pub fn ensureDeclAnalyzed(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Zcu.Sem }; } - const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0); + const decl_prog_node = mod.sema_prog_node.start(decl.fqn.toSlice(ip), 0); defer decl_prog_node.end(); break :blk pt.semaDecl(decl_index) catch |err| switch (err) { @@ -747,10 +756,9 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai defer liveness.deinit(gpa); if (build_options.enable_debug_extensions and comp.verbose_air) { - const fqn = try decl.fullyQualifiedName(pt); - std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}); + std.debug.print("# Begin Function AIR: {}:\n", .{decl.fqn.fmt(ip)}); @import("../print_air.zig").dump(pt, air, liveness); - std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}); + std.debug.print("# End Function AIR: {}\n\n", .{decl.fqn.fmt(ip)}); } if (std.debug.runtime_safety) { @@ -781,7 +789,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai }; } - const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0); + const codegen_prog_node = zcu.codegen_prog_node.start(decl.fqn.toSlice(ip), 0); defer codegen_prog_node.end(); if (!air.typesFullyResolved(zcu)) { @@ -818,7 +826,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai /// https://github.com/ziglang/zig/issues/14307 pub fn semaPkg(pt: Zcu.PerThread, pkg: *Module) !void { - const import_file_result = try pt.zcu.importPkg(pkg); + const import_file_result = try pt.importPkg(pkg); const root_decl_index = pt.zcu.fileRootDecl(import_file_result.file_index); if (root_decl_index == .none) { return pt.semaFile(import_file_result.file_index); @@ -855,7 +863,10 @@ fn getFileRootStruct( const decls = file.zir.bodySlice(extra_index, decls_len); extra_index += decls_len; - const tracked_inst = try ip.trackZir(gpa, file_index, .main_struct_inst); + const tracked_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }); const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{ .layout = .auto, .fields_len = fields_len, @@ -996,8 +1007,8 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { zcu.setFileRootDecl(file_index, new_decl_index.toOptional()); zcu.namespacePtr(new_namespace_index).decl_index = new_decl_index; - new_decl.name = try file.fullyQualifiedName(pt); - new_decl.name_fully_qualified = true; + new_decl.fqn = try file.internFullyQualifiedName(pt); + new_decl.name = new_decl.fqn; new_decl.is_pub = true; new_decl.is_exported = false; new_decl.alignment = .none; @@ -1016,7 +1027,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { switch (zcu.comp.cache_use) { .whole => |whole| if (whole.cache_manifest) |man| { const source = file.getSource(gpa) catch |err| { - try Zcu.reportRetryableFileError(zcu, file_index, "unable to load source: {s}", .{@errorName(err)}); + try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)}); return error.AnalysisFail; }; @@ -1025,7 +1036,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { file.mod.root.sub_path, file.sub_file_path, }) catch |err| { - try Zcu.reportRetryableFileError(zcu, file_index, "unable to resolve path: {s}", .{@errorName(err)}); + try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)}); return error.AnalysisFail; }; errdefer gpa.free(resolved_path); @@ -1058,10 +1069,8 @@ fn semaDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !Zcu.SemaDeclResult { } log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)}); - log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(pt)).fmt(ip)}); - defer blk: { - log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(pt) catch break :blk).fmt(ip)}); - } + log.debug("decl name '{}'", .{decl.fqn.fmt(ip)}); + defer log.debug("finish decl name '{}'", .{decl.fqn.fmt(ip)}); const old_has_tv = decl.has_tv; // The following values are ignored if `!old_has_tv` @@ -1084,7 +1093,7 @@ fn semaDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !Zcu.SemaDeclResult { const std_mod = zcu.std_mod; if (decl.getFileScope(zcu).mod != std_mod) break :ip_index .none; // We're in the std module. - const std_file_imported = try zcu.importPkg(std_mod); + const std_file_imported = try pt.importPkg(std_mod); const std_file_root_decl_index = zcu.fileRootDecl(std_file_imported.file_index); const std_decl = zcu.declPtr(std_file_root_decl_index.unwrap().?); const std_namespace = std_decl.getInnerNamespace(zcu).?; @@ -1151,11 +1160,10 @@ fn semaDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !Zcu.SemaDeclResult { defer sema.deinit(); // Every Decl (other than file root Decls, which do not have a ZIR index) has a dependency on its own source. - try sema.declareDependency(.{ .src_hash = try ip.trackZir( - gpa, - decl.getFileScopeIndex(zcu), - decl_inst, - ) }); + try sema.declareDependency(.{ .src_hash = try ip.trackZir(gpa, pt.tid, .{ + .file = decl.getFileScopeIndex(zcu), + .inst = decl_inst, + }) }); var block_scope: Sema.Block = .{ .parent = null, @@ -1359,6 +1367,195 @@ pub fn semaAnonOwnerDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !Zcu.Sem }; } +pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { + const zcu = pt.zcu; + const gpa = zcu.gpa; + + // The resolved path is used as the key in the import table, to detect if + // an import refers to the same as another, despite different relative paths + // or differently mapped package names. + const resolved_path = try std.fs.path.resolve(gpa, &.{ + mod.root.root_dir.path orelse ".", + mod.root.sub_path, + mod.root_src_path, + }); + var keep_resolved_path = false; + defer if (!keep_resolved_path) gpa.free(resolved_path); + + const gop = try zcu.import_table.getOrPut(gpa, resolved_path); + errdefer _ = zcu.import_table.pop(); + if (gop.found_existing) { + const file_index = gop.value_ptr.*; + const file = zcu.fileByIndex(file_index); + try file.addReference(zcu, .{ .root = mod }); + return .{ + .file = file, + .file_index = file_index, + .is_new = false, + .is_pkg = true, + }; + } + + const ip = &zcu.intern_pool; + if (mod.builtin_file) |builtin_file| { + const path_digest = Zcu.computePathDigest(zcu, mod, builtin_file.sub_file_path); + const file_index = try ip.createFile(gpa, pt.tid, .{ + .bin_digest = path_digest, + .file = builtin_file, + .root_decl = .none, + }); + keep_resolved_path = true; // It's now owned by import_table. + gop.value_ptr.* = file_index; + try builtin_file.addReference(zcu, .{ .root = mod }); + return .{ + .file = builtin_file, + .file_index = file_index, + .is_new = false, + .is_pkg = true, + }; + } + + const sub_file_path = try gpa.dupe(u8, mod.root_src_path); + errdefer gpa.free(sub_file_path); + + const new_file = try gpa.create(Zcu.File); + errdefer gpa.destroy(new_file); + + const path_digest = zcu.computePathDigest(mod, sub_file_path); + const new_file_index = try ip.createFile(gpa, pt.tid, .{ + .bin_digest = path_digest, + .file = new_file, + .root_decl = .none, + }); + keep_resolved_path = true; // It's now owned by import_table. + gop.value_ptr.* = new_file_index; + new_file.* = .{ + .sub_file_path = sub_file_path, + .source = undefined, + .source_loaded = false, + .tree_loaded = false, + .zir_loaded = false, + .stat = undefined, + .tree = undefined, + .zir = undefined, + .status = .never_loaded, + .mod = mod, + }; + + try new_file.addReference(zcu, .{ .root = mod }); + return .{ + .file = new_file, + .file_index = new_file_index, + .is_new = true, + .is_pkg = true, + }; +} + +/// Called from a worker thread during AstGen. +/// Also called from Sema during semantic analysis. +pub fn importFile( + pt: Zcu.PerThread, + cur_file: *Zcu.File, + import_string: []const u8, +) !Zcu.ImportFileResult { + const zcu = pt.zcu; + const mod = cur_file.mod; + + if (std.mem.eql(u8, import_string, "std")) { + return pt.importPkg(zcu.std_mod); + } + if (std.mem.eql(u8, import_string, "root")) { + return pt.importPkg(zcu.root_mod); + } + if (mod.deps.get(import_string)) |pkg| { + return pt.importPkg(pkg); + } + if (!std.mem.endsWith(u8, import_string, ".zig")) { + return error.ModuleNotFound; + } + const gpa = zcu.gpa; + + // The resolved path is used as the key in the import table, to detect if + // an import refers to the same as another, despite different relative paths + // or differently mapped package names. + const resolved_path = try std.fs.path.resolve(gpa, &.{ + mod.root.root_dir.path orelse ".", + mod.root.sub_path, + cur_file.sub_file_path, + "..", + import_string, + }); + + var keep_resolved_path = false; + defer if (!keep_resolved_path) gpa.free(resolved_path); + + const gop = try zcu.import_table.getOrPut(gpa, resolved_path); + errdefer _ = zcu.import_table.pop(); + if (gop.found_existing) { + const file_index = gop.value_ptr.*; + return .{ + .file = zcu.fileByIndex(file_index), + .file_index = file_index, + .is_new = false, + .is_pkg = false, + }; + } + + const ip = &zcu.intern_pool; + + const new_file = try gpa.create(Zcu.File); + errdefer gpa.destroy(new_file); + + const resolved_root_path = try std.fs.path.resolve(gpa, &.{ + mod.root.root_dir.path orelse ".", + mod.root.sub_path, + }); + defer gpa.free(resolved_root_path); + + const sub_file_path = p: { + const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path); + errdefer gpa.free(relative); + + if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) { + break :p relative; + } + return error.ImportOutsideModulePath; + }; + errdefer gpa.free(sub_file_path); + + log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{ + resolved_root_path, resolved_path, sub_file_path, import_string, + }); + + const path_digest = zcu.computePathDigest(mod, sub_file_path); + const new_file_index = try ip.createFile(gpa, pt.tid, .{ + .bin_digest = path_digest, + .file = new_file, + .root_decl = .none, + }); + keep_resolved_path = true; // It's now owned by import_table. + gop.value_ptr.* = new_file_index; + new_file.* = .{ + .sub_file_path = sub_file_path, + .source = undefined, + .source_loaded = false, + .tree_loaded = false, + .zir_loaded = false, + .stat = undefined, + .tree = undefined, + .zir = undefined, + .status = .never_loaded, + .mod = mod, + }; + + return .{ + .file = new_file, + .file_index = new_file_index, + .is_new = true, + .is_pkg = false, + }; +} + pub fn embedFile( pt: Zcu.PerThread, cur_file: *Zcu.File, @@ -1432,20 +1629,6 @@ pub fn embedFile( return pt.newEmbedFile(cur_file.mod, sub_file_path, resolved_path, gop.value_ptr, src_loc); } -/// Cancel the creation of an anon decl and delete any references to it. -/// If other decls depend on this decl, they must be aborted first. -pub fn abortAnonDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) void { - assert(!pt.zcu.declIsRoot(decl_index)); - pt.destroyDecl(decl_index); -} - -/// Finalize the creation of an anon decl. -pub fn finalizeAnonDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Allocator.Error!void { - if (pt.zcu.declPtr(decl_index).typeOf(pt.zcu).isFnOrHasRuntimeBits(pt)) { - try pt.zcu.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); - } -} - /// https://github.com/ziglang/zig/issues/14307 fn newEmbedFile( pt: Zcu.PerThread, @@ -1718,7 +1901,10 @@ const ScanDeclIter = struct { } const parent_file_scope_index = iter.parent_decl.getFileScopeIndex(zcu); - const tracked_inst = try ip.trackZir(gpa, parent_file_scope_index, decl_inst); + const tracked_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = parent_file_scope_index, + .inst = decl_inst, + }); // We create a Decl for it regardless of analysis status. @@ -1728,6 +1914,7 @@ const ScanDeclIter = struct { const was_exported = decl.is_exported; assert(decl.kind == kind); // ZIR tracking should preserve this decl.name = decl_name; + decl.fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, decl_name); decl.is_pub = declaration.flags.is_pub; decl.is_exported = declaration.flags.is_export; break :decl_index .{ was_exported, decl_index }; @@ -1737,6 +1924,7 @@ const ScanDeclIter = struct { const new_decl = zcu.declPtr(new_decl_index); new_decl.kind = kind; new_decl.name = decl_name; + new_decl.fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, decl_name); new_decl.is_pub = declaration.flags.is_pub; new_decl.is_exported = declaration.flags.is_export; new_decl.zir_decl_index = tracked_inst.toOptional(); @@ -1761,10 +1949,9 @@ const ScanDeclIter = struct { if (!comp.config.is_test) break :a false; if (decl_mod != zcu.main_mod) break :a false; if (is_named_test and comp.test_filters.len > 0) { - const decl_fqn = try namespace.fullyQualifiedName(pt, decl_name); - const decl_fqn_slice = decl_fqn.toSlice(ip); + const decl_fqn = decl.fqn.toSlice(ip); for (comp.test_filters) |test_filter| { - if (std.mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break; + if (std.mem.indexOf(u8, decl_fqn, test_filter)) |_| break; } else break :a false; } zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update @@ -1794,6 +1981,20 @@ const ScanDeclIter = struct { } }; +/// Cancel the creation of an anon decl and delete any references to it. +/// If other decls depend on this decl, they must be aborted first. +pub fn abortAnonDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) void { + assert(!pt.zcu.declIsRoot(decl_index)); + pt.destroyDecl(decl_index); +} + +/// Finalize the creation of an anon decl. +pub fn finalizeAnonDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Allocator.Error!void { + if (pt.zcu.declPtr(decl_index).typeOf(pt.zcu).isFnOrHasRuntimeBits(pt)) { + try pt.zcu.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); + } +} + pub fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index, arena: Allocator) Zcu.SemaError!Air { const tracy = trace(@src()); defer tracy.end(); @@ -1805,12 +2006,10 @@ pub fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index, arena: All const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); - log.debug("func name '{}'", .{(try decl.fullyQualifiedName(pt)).fmt(ip)}); - defer blk: { - log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(pt) catch break :blk).fmt(ip)}); - } + log.debug("func name '{}'", .{decl.fqn.fmt(ip)}); + defer log.debug("finish func name '{}'", .{decl.fqn.fmt(ip)}); - const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0); + const decl_prog_node = mod.sema_prog_node.start(decl.fqn.toSlice(ip), 0); defer decl_prog_node.end(); mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index })); @@ -1911,10 +2110,17 @@ pub fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index, arena: All runtime_params_len; var runtime_param_index: usize = 0; - for (fn_info.param_body[0..src_params_len], 0..) |inst, src_param_index| { + for (fn_info.param_body[0..src_params_len]) |inst| { const gop = sema.inst_map.getOrPutAssumeCapacity(inst); if (gop.found_existing) continue; // provided above by comptime arg + const inst_info = sema.code.instructions.get(@intFromEnum(inst)); + const param_name: Zir.NullTerminatedString = switch (inst_info.tag) { + .param_anytype => inst_info.data.str_tok.start, + .param => sema.code.extraData(Zir.Inst.Param, inst_info.data.pl_tok.payload_index).data.name, + else => unreachable, + }; + const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index]; runtime_param_index += 1; @@ -1935,7 +2141,10 @@ pub fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index, arena: All .tag = .arg, .data = .{ .arg = .{ .ty = Air.internedToRef(param_ty), - .src_index = @intCast(src_param_index), + .name = if (inner_block.ownerModule().strip) + .none + else + @enumFromInt(try sema.appendAirString(sema.code.nullTerminatedString(param_name))), } }, }); } @@ -2053,6 +2262,7 @@ pub fn allocateNewDecl(pt: Zcu.PerThread, namespace: Zcu.Namespace.Index) !Zcu.D const gpa = zcu.gpa; const decl_index = try zcu.intern_pool.createDecl(gpa, pt.tid, .{ .name = undefined, + .fqn = undefined, .src_namespace = namespace, .has_tv = false, .owns_tv = false, @@ -2077,6 +2287,36 @@ pub fn allocateNewDecl(pt: Zcu.PerThread, namespace: Zcu.Namespace.Index) !Zcu.D return decl_index; } +pub fn getErrorValue( + pt: Zcu.PerThread, + name: InternPool.NullTerminatedString, +) Allocator.Error!Zcu.ErrorInt { + return pt.zcu.intern_pool.getErrorValue(pt.zcu.gpa, pt.tid, name); +} + +pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Zcu.ErrorInt { + return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(pt.zcu.gpa, name)); +} + +pub fn initNewAnonDecl( + pt: Zcu.PerThread, + new_decl_index: Zcu.Decl.Index, + val: Value, + name: InternPool.NullTerminatedString, + fqn: InternPool.OptionalNullTerminatedString, +) Allocator.Error!void { + const new_decl = pt.zcu.declPtr(new_decl_index); + + new_decl.name = name; + new_decl.fqn = fqn.unwrap() orelse try pt.zcu.namespacePtr(new_decl.src_namespace) + .internFullyQualifiedName(&pt.zcu.intern_pool, pt.zcu.gpa, pt.tid, name); + new_decl.val = val; + new_decl.alignment = .none; + new_decl.@"linksection" = .none; + new_decl.has_tv = true; + new_decl.analysis = .complete; +} + fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void { switch (file.status) { .success_zir, .retryable_failure => {}, @@ -2229,7 +2469,7 @@ pub fn populateTestFunctions( const gpa = zcu.gpa; const ip = &zcu.intern_pool; const builtin_mod = zcu.root_mod.getBuiltinDependency(); - const builtin_file_index = (zcu.importPkg(builtin_mod) catch unreachable).file_index; + const builtin_file_index = (pt.importPkg(builtin_mod) catch unreachable).file_index; const root_decl_index = zcu.fileRootDecl(builtin_file_index); const root_decl = zcu.declPtr(root_decl_index.unwrap().?); const builtin_namespace = zcu.namespacePtr(root_decl.src_namespace); @@ -2260,7 +2500,7 @@ pub fn populateTestFunctions( for (test_fn_vals, zcu.test_functions.keys()) |*test_fn_val, test_decl_index| { const test_decl = zcu.declPtr(test_decl_index); - const test_decl_name = try test_decl.fullyQualifiedName(pt); + const test_decl_name = test_decl.fqn; const test_decl_name_len = test_decl_name.length(ip); const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: { const test_name_ty = try pt.arrayType(.{ @@ -2366,7 +2606,7 @@ pub fn linkerUpdateDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !void { const decl = zcu.declPtr(decl_index); - const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(&zcu.intern_pool), 0); + const codegen_prog_node = zcu.codegen_prog_node.start(decl.fqn.toSlice(&zcu.intern_pool), 0); defer codegen_prog_node.end(); if (comp.bin_file) |lf| { @@ -2396,6 +2636,87 @@ pub fn linkerUpdateDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !void { } } +pub fn reportRetryableAstGenError( + pt: Zcu.PerThread, + src: Zcu.AstGenSrc, + file_index: Zcu.File.Index, + err: anyerror, +) error{OutOfMemory}!void { + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + + const file = zcu.fileByIndex(file_index); + file.status = .retryable_failure; + + const src_loc: Zcu.LazySrcLoc = switch (src) { + .root => .{ + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }), + .offset = .entire_file, + }, + .import => |info| .{ + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = info.importing_file, + .inst = .main_struct_inst, + }), + .offset = .{ .token_abs = info.import_tok }, + }, + }; + + const err_msg = try Zcu.ErrorMsg.create(gpa, src_loc, "unable to load '{}{s}': {s}", .{ + file.mod.root, file.sub_file_path, @errorName(err), + }); + errdefer err_msg.destroy(gpa); + + { + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + try zcu.failed_files.putNoClobber(gpa, file, err_msg); + } +} + +pub fn reportRetryableFileError( + pt: Zcu.PerThread, + file_index: Zcu.File.Index, + comptime format: []const u8, + args: anytype, +) error{OutOfMemory}!void { + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + + const file = zcu.fileByIndex(file_index); + file.status = .retryable_failure; + + const err_msg = try Zcu.ErrorMsg.create( + gpa, + .{ + .base_node_inst = try ip.trackZir(gpa, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }), + .offset = .entire_file, + }, + format, + args, + ); + errdefer err_msg.destroy(gpa); + + zcu.comp.mutex.lock(); + defer zcu.comp.mutex.unlock(); + + const gop = try zcu.failed_files.getOrPut(gpa, file); + if (gop.found_existing) { + if (gop.value_ptr.*) |old_err_msg| { + old_err_msg.destroy(gpa); + } + } + gop.value_ptr.* = err_msg; +} + /// Shortcut for calling `intern_pool.get`. pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index { return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key); @@ -2897,7 +3218,7 @@ pub fn getBuiltinDecl(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Inter const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const std_file_imported = zcu.importPkg(zcu.std_mod) catch @panic("failed to import lib/std.zig"); + const std_file_imported = pt.importPkg(zcu.std_mod) catch @panic("failed to import lib/std.zig"); const std_file_root_decl = zcu.fileRootDecl(std_file_imported.file_index).unwrap().?; const std_namespace = zcu.declPtr(std_file_root_decl).getOwnedInnerNamespace(zcu).?; const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 7a2c0178cd..8a020fc521 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4231,19 +4231,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const pt = self.pt; - const mod = pt.zcu; const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; - const name = mod.getParamName(self.func_index, src_index); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); + const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name_nts != .none) { + const name = self.air.nullTerminatedString(@intFromEnum(name_nts)); + try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name, + .mcv = self.args[arg_index], + }); + } const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0dd513d4fe..f923c001e1 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4206,19 +4206,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const pt = self.pt; - const mod = pt.zcu; const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; - const name = mod.getParamName(self.func_index, src_index); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); + const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name_nts != .none) { + const name = self.air.nullTerminatedString(@intFromEnum(name_nts)); + try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name, + .mcv = self.args[arg_index], + }); + } const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f4f0ff0972..70876a298b 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -933,7 +933,7 @@ fn formatDecl( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - try data.mod.declPtr(data.decl_index).renderFullyQualifiedName(data.mod, writer); + try writer.print("{}", .{data.mod.declPtr(data.decl_index).fqn.fmt(&data.mod.intern_pool)}); } fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { return .{ .data = .{ @@ -4051,7 +4051,8 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); const owner_decl = zcu.funcOwnerDeclIndex(func.func_index); - const name = zcu.getParamName(func.func_index, arg.src_index); + if (arg.name == .none) return; + const name = func.air.nullTerminatedString(@intFromEnum(arg.name)); switch (func.debug_output) { .dwarf => |dw| switch (mcv) { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 6dea497753..1c3b2327b6 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -3614,7 +3614,8 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); const owner_decl = mod.funcOwnerDeclIndex(self.func_index); - const name = mod.getParamName(self.func_index, arg.src_index); + if (arg.name == .none) return; + const name = self.air.nullTerminatedString(@intFromEnum(arg.name)); switch (self.debug_output) { .dwarf => |dw| switch (mcv) { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8873c5cb1b..32b3b42389 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2585,11 +2585,13 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { switch (func.debug_output) { .dwarf => |dwarf| { - const src_index = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; - const name = mod.getParamName(func.func_index, src_index); - try dwarf.genArgDbgInfo(name, arg_ty, mod.funcOwnerDeclIndex(func.func_index), .{ - .wasm_local = arg.local.value, - }); + const name_nts = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name_nts != .none) { + const name = func.air.nullTerminatedString(@intFromEnum(name_nts)); + try dwarf.genArgDbgInfo(name, arg_ty, mod.funcOwnerDeclIndex(func.func_index), .{ + .wasm_local = arg.local.value, + }); + } }, else => {}, } @@ -3302,7 +3304,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { } }, .err => |err| { - const int = try mod.getErrorValue(err.name); + const int = try pt.getErrorValue(err.name); return WValue{ .imm32 = int }; }, .error_union => |error_union| { @@ -3450,30 +3452,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { /// Returns a `Value` as a signed 32 bit value. /// It's illegal to provide a value with a type that cannot be represented /// as an integer value. -fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 { +fn valueAsI32(func: *const CodeGen, val: Value) i32 { const pt = func.pt; const mod = pt.zcu; + const ip = &mod.intern_pool; - switch (val.ip_index) { - .none => {}, + switch (val.toIntern()) { .bool_true => return 1, .bool_false => return 0, - else => return switch (mod.intern_pool.indexToKey(val.ip_index)) { - .enum_tag => |enum_tag| intIndexAsI32(&mod.intern_pool, enum_tag.int, pt), + else => return switch (ip.indexToKey(val.ip_index)) { + .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, pt), .int => |int| intStorageAsI32(int.storage, pt), .ptr => |ptr| { assert(ptr.base_addr == .int); return @intCast(ptr.byte_offset); }, - .err => |err| @as(i32, @bitCast(@as(Zcu.ErrorInt, @intCast(mod.global_error_set.getIndex(err.name).?)))), + .err => |err| @bitCast(ip.getErrorValueIfExists(err.name).?), else => unreachable, }, } - - return switch (ty.zigTypeTag(mod)) { - .ErrorSet => @as(i32, @bitCast(val.getErrorInt(mod))), - else => unreachable, // Programmer called this function for an illegal type - }; } fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, pt: Zcu.PerThread) i32 { @@ -4096,7 +4093,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { for (items, 0..) |ref, i| { const item_val = (try func.air.value(ref, pt)).?; - const int_val = func.valueAsI32(item_val, target_ty); + const int_val = func.valueAsI32(item_val); if (lowest_maybe == null or int_val < lowest_maybe.?) { lowest_maybe = int_val; } @@ -7284,8 +7281,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const fqn = try mod.declPtr(enum_decl_index).fullyQualifiedName(pt); - const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{fqn.fmt(ip)}); + const decl = mod.declPtr(enum_decl_index); + const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{decl.fqn.fmt(ip)}); // check if we already generated code for this. if (func.bin_file.findGlobalSymbol(func_name)) |loc| { @@ -7452,7 +7449,7 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { var lowest: ?u32 = null; var highest: ?u32 = null; for (0..names.len) |name_index| { - const err_int: Zcu.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[name_index]).?); + const err_int = ip.getErrorValueIfExists(names.get(ip)[name_index]).?; if (lowest) |*l| { if (err_int < l.*) { l.* = err_int; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index dafeed00b8..92aac552d8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1077,7 +1077,7 @@ fn formatDecl( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - try data.zcu.declPtr(data.decl_index).renderFullyQualifiedName(data.zcu, writer); + try writer.print("{}", .{data.zcu.declPtr(data.decl_index).fqn.fmt(&data.zcu.intern_pool)}); } fn fmtDecl(self: *Self, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { return .{ .data = .{ @@ -11920,9 +11920,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { else => return self.fail("TODO implement arg for {}", .{src_mcv}), }; - const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; - const name = mod.getParamName(self.owner.func_index, src_index); - try self.genArgDbgInfo(arg_ty, name, src_mcv); + const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + switch (name_nts) { + .none => {}, + _ => try self.genArgDbgInfo(arg_ty, self.air.nullTerminatedString(@intFromEnum(name_nts)), src_mcv), + } break :result dst_mcv; }; @@ -16433,7 +16435,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { .size = .dword, .index = err_reg.to64(), .scale = .@"4", - .disp = 4, + .disp = (1 - 1) * 4, } }, }, ); @@ -16446,7 +16448,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { .size = .dword, .index = err_reg.to64(), .scale = .@"4", - .disp = 8, + .disp = (2 - 1) * 4, } }, }, ); diff --git a/src/codegen.zig b/src/codegen.zig index 0513682d73..d05cb42728 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -137,10 +137,10 @@ pub fn generateLazySymbol( if (lazy_sym.ty.isAnyError(pt.zcu)) { alignment.* = .@"4"; - const err_names = pt.zcu.global_error_set.keys(); + const err_names = ip.global_error_set.getNamesFromMainThread(); mem.writeInt(u32, try code.addManyAsArray(4), @intCast(err_names.len), endian); var offset = code.items.len; - try code.resize((1 + err_names.len + 1) * 4); + try code.resize((err_names.len + 1) * 4); for (err_names) |err_name_nts| { const err_name = err_name_nts.toSlice(ip); mem.writeInt(u32, code.items[offset..][0..4], @intCast(code.items.len), endian); @@ -243,13 +243,13 @@ pub fn generateSymbol( int_val.writeTwosComplement(try code.addManyAsSlice(abi_size), endian); }, .err => |err| { - const int = try mod.getErrorValue(err.name); + const int = try pt.getErrorValue(err.name); try code.writer().writeInt(u16, @intCast(int), endian); }, .error_union => |error_union| { const payload_ty = ty.errorUnionPayload(mod); const err_val: u16 = switch (error_union.val) { - .err_name => |err_name| @intCast(try mod.getErrorValue(err_name)), + .err_name => |err_name| @intCast(try pt.getErrorValue(err_name)), .payload => 0, }; @@ -1058,7 +1058,7 @@ pub fn genTypedValue( }, .ErrorSet => { const err_name = ip.indexToKey(val.toIntern()).err.name; - const error_index = zcu.global_error_set.getIndex(err_name).?; + const error_index = try pt.getErrorValue(err_name); return GenResult.mcv(.{ .immediate = error_index }); }, .ErrorUnion => { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2fa8a98cbb..60b07f0e3f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2194,13 +2194,9 @@ pub const DeclGen = struct { }) else { // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), // expand to 3x the length of its input, but let's cut it off at a much shorter limit. - var name: [100]u8 = undefined; - var name_stream = std.io.fixedBufferStream(&name); - decl.renderFullyQualifiedName(zcu, name_stream.writer()) catch |err| switch (err) { - error.NoSpaceLeft => {}, - }; + const fqn_slice = decl.fqn.toSlice(ip); try writer.print("{}__{d}", .{ - fmtIdent(name_stream.getWritten()), + fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]), @intFromEnum(decl_index), }); } @@ -2587,11 +2583,9 @@ pub fn genTypeDecl( try writer.writeByte(';'); const owner_decl = zcu.declPtr(owner_decl_index); const owner_mod = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu).mod; - if (!owner_mod.strip) { - try writer.writeAll(" /* "); - try owner_decl.renderFullyQualifiedName(zcu, writer); - try writer.writeAll(" */"); - } + if (!owner_mod.strip) try writer.print(" /* {} */", .{ + owner_decl.fqn.fmt(&zcu.intern_pool), + }); try writer.writeByte('\n'); }, }, @@ -2628,10 +2622,11 @@ pub fn genErrDecls(o: *Object) !void { var max_name_len: usize = 0; // do not generate an invalid empty enum when the global error set is empty - if (zcu.global_error_set.keys().len > 1) { + const names = ip.global_error_set.getNamesFromMainThread(); + if (names.len > 0) { try writer.writeAll("enum {\n"); o.indent_writer.pushIndent(); - for (zcu.global_error_set.keys()[1..], 1..) |name_nts, value| { + for (names, 1..) |name_nts, value| { const name = name_nts.toSlice(ip); max_name_len = @max(name.len, max_name_len); const err_val = try pt.intern(.{ .err = .{ @@ -2650,7 +2645,7 @@ pub fn genErrDecls(o: *Object) !void { defer o.dg.gpa.free(name_buf); @memcpy(name_buf[0..name_prefix.len], name_prefix); - for (zcu.global_error_set.keys()) |name| { + for (names) |name| { const name_slice = name.toSlice(ip); @memcpy(name_buf[name_prefix.len..][0..name_slice.len], name_slice); const identifier = name_buf[0 .. name_prefix.len + name_slice.len]; @@ -2680,7 +2675,7 @@ pub fn genErrDecls(o: *Object) !void { } const name_array_ty = try pt.arrayType(.{ - .len = zcu.global_error_set.count(), + .len = 1 + names.len, .child = .slice_const_u8_sentinel_0_type, }); @@ -2694,9 +2689,9 @@ pub fn genErrDecls(o: *Object) !void { .complete, ); try writer.writeAll(" = {"); - for (zcu.global_error_set.keys(), 0..) |name_nts, value| { + for (names, 1..) |name_nts, val| { const name = name_nts.toSlice(ip); - if (value != 0) try writer.writeByte(','); + if (val > 1) try writer.writeAll(", "); try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ fmtIdent(name), try o.dg.fmtIntLiteral(try pt.intValue(Type.usize, name.len), .StaticInitializer), @@ -4563,9 +4558,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func); const writer = f.object.writer(); - try writer.writeAll("/* inline:"); - try owner_decl.renderFullyQualifiedName(zcu, writer); - try writer.writeAll(" */\n"); + try writer.print("/* inline:{} */\n", .{owner_decl.fqn.fmt(&zcu.intern_pool)}); return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len])); } @@ -6881,7 +6874,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = zig_errorName["); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("];\n"); + try writer.writeAll(" - 1];\n"); return local; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0f13c9fd9b..abc6a9dc9d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1036,20 +1036,21 @@ pub const Object = struct { const pt = o.pt; const mod = pt.zcu; + const ip = &mod.intern_pool; - const error_name_list = mod.global_error_set.keys(); - const llvm_errors = try mod.gpa.alloc(Builder.Constant, error_name_list.len); + const error_name_list = ip.global_error_set.getNamesFromMainThread(); + const llvm_errors = try mod.gpa.alloc(Builder.Constant, 1 + error_name_list.len); defer mod.gpa.free(llvm_errors); // TODO: Address space const slice_ty = Type.slice_const_u8_sentinel_0; const llvm_usize_ty = try o.lowerType(Type.usize); const llvm_slice_ty = try o.lowerType(slice_ty); - const llvm_table_ty = try o.builder.arrayType(error_name_list.len, llvm_slice_ty); + const llvm_table_ty = try o.builder.arrayType(1 + error_name_list.len, llvm_slice_ty); llvm_errors[0] = try o.builder.undefConst(llvm_slice_ty); - for (llvm_errors[1..], error_name_list[1..]) |*llvm_error, name| { - const name_string = try o.builder.stringNull(name.toSlice(&mod.intern_pool)); + for (llvm_errors[1..], error_name_list) |*llvm_error, name| { + const name_string = try o.builder.stringNull(name.toSlice(ip)); const name_init = try o.builder.stringConst(name_string); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); @@ -1085,7 +1086,7 @@ pub const Object = struct { // If there is no such function in the module, it means the source code does not need it. const name = o.builder.strtabStringIfExists(lt_errors_fn_name) orelse return; const llvm_fn = o.builder.getGlobal(name) orelse return; - const errors_len = o.pt.zcu.global_error_set.count(); + const errors_len = o.pt.zcu.intern_pool.global_error_set.mutate.list.len; var wip = try Builder.WipFunction.init(&o.builder, .{ .function = llvm_fn.ptrConst(&o.builder).kind.function, @@ -1096,12 +1097,12 @@ pub const Object = struct { // Example source of the following LLVM IR: // fn __zig_lt_errors_len(index: u16) bool { - // return index < total_errors_len; + // return index <= total_errors_len; // } const lhs = wip.arg(0); const rhs = try o.builder.intValue(try o.errorIntType(), errors_len); - const is_lt = try wip.icmp(.ult, lhs, rhs, ""); + const is_lt = try wip.icmp(.ule, lhs, rhs, ""); _ = try wip.ret(is_lt); try wip.finish(); } @@ -1744,7 +1745,7 @@ pub const Object = struct { if (export_indices.len != 0) { return updateExportedGlobal(self, zcu, global_index, export_indices); } else { - const fqn = try self.builder.strtabString((try decl.fullyQualifiedName(pt)).toSlice(ip)); + const fqn = try self.builder.strtabString(decl.fqn.toSlice(ip)); try global_index.rename(fqn, &self.builder); global_index.setLinkage(.internal, &self.builder); if (comp.config.dll_export_fns) @@ -2811,7 +2812,7 @@ pub const Object = struct { const zcu = pt.zcu; const std_mod = zcu.std_mod; - const std_file_imported = zcu.importPkg(std_mod) catch unreachable; + const std_file_imported = pt.importPkg(std_mod) catch unreachable; const builtin_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "builtin", .no_embedded_nulls); const std_file_root_decl = zcu.fileRootDecl(std_file_imported.file_index); @@ -2863,10 +2864,7 @@ pub const Object = struct { const is_extern = decl.isExtern(zcu); const function_index = try o.builder.addFunction( try o.lowerType(zig_fn_type), - try o.builder.strtabString((if (is_extern) - decl.name - else - try decl.fullyQualifiedName(pt)).toSlice(ip)), + try o.builder.strtabString((if (is_extern) decl.name else decl.fqn).toSlice(ip)), toLlvmAddressSpace(decl.@"addrspace", target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -3077,14 +3075,12 @@ pub const Object = struct { const pt = o.pt; const zcu = pt.zcu; + const ip = &zcu.intern_pool; const decl = zcu.declPtr(decl_index); const is_extern = decl.isExtern(zcu); const variable_index = try o.builder.addVariable( - try o.builder.strtabString((if (is_extern) - decl.name - else - try decl.fullyQualifiedName(pt)).toSlice(&zcu.intern_pool)), + try o.builder.strtabString((if (is_extern) decl.name else decl.fqn).toSlice(ip)), try o.lowerType(decl.typeOf(zcu)), toLlvmGlobalAddressSpace(decl.@"addrspace", zcu.getTarget()), ); @@ -3312,7 +3308,7 @@ pub const Object = struct { return int_ty; } - const fqn = try mod.declPtr(struct_type.decl.unwrap().?).fullyQualifiedName(pt); + const decl = mod.declPtr(struct_type.decl.unwrap().?); var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){}; defer llvm_field_types.deinit(o.gpa); @@ -3377,7 +3373,7 @@ pub const Object = struct { ); } - const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3466,7 +3462,7 @@ pub const Object = struct { return enum_tag_ty; } - const fqn = try mod.declPtr(union_obj.decl).fullyQualifiedName(pt); + const decl = mod.declPtr(union_obj.decl); const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]); const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty); @@ -3486,7 +3482,7 @@ pub const Object = struct { }; if (layout.tag_size == 0) { - const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3514,7 +3510,7 @@ pub const Object = struct { llvm_fields_len += 1; } - const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3527,8 +3523,7 @@ pub const Object = struct { const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); if (!gop.found_existing) { const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl); - const fqn = try decl.fullyQualifiedName(pt); - gop.value_ptr.* = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); + gop.value_ptr.* = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip))); } return gop.value_ptr.*; }, @@ -3826,7 +3821,7 @@ pub const Object = struct { return lowerBigInt(o, ty, bigint); }, .err => |err| { - const int = try mod.getErrorValue(err.name); + const int = try pt.getErrorValue(err.name); const llvm_int = try o.builder.intConst(try o.errorIntType(), int); return llvm_int; }, @@ -4587,11 +4582,11 @@ pub const Object = struct { const usize_ty = try o.lowerType(Type.usize); const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); - const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(pt); + const decl = zcu.declPtr(enum_type.decl); const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.strtabStringFmt("__zig_tag_name_{}", .{fqn.fmt(ip)}), + try o.builder.strtabStringFmt("__zig_tag_name_{}", .{decl.fqn.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); @@ -5175,8 +5170,6 @@ pub const FuncGen = struct { const line_number = decl.navSrcLine(zcu) + 1; self.inlined = self.wip.debug_location; - const fqn = try decl.fullyQualifiedName(pt); - const fn_ty = try pt.funcType(.{ .param_types = &.{}, .return_type = .void_type, @@ -5185,7 +5178,7 @@ pub const FuncGen = struct { self.scope = try o.builder.debugSubprogram( self.file, try o.builder.metadataString(decl.name.toSlice(&zcu.intern_pool)), - try o.builder.metadataString(fqn.toSlice(&zcu.intern_pool)), + try o.builder.metadataString(decl.fqn.toSlice(&zcu.intern_pool)), line_number, line_number + func.lbrace_line, try o.lowerDebugType(fn_ty), @@ -8867,19 +8860,21 @@ pub const FuncGen = struct { self.arg_index += 1; // llvm does not support debug info for naked function arguments - if (self.wip.strip or self.is_naked) return arg_val; + if (self.is_naked) return arg_val; const inst_ty = self.typeOfIndex(inst); if (needDbgVarWorkaround(o)) return arg_val; - const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; + const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name == .none) return arg_val; + const func_index = self.dg.decl.getOwnedFunctionIndex(); const func = mod.funcInfo(func_index); const lbrace_line = mod.declPtr(func.owner_decl).navSrcLine(mod) + func.lbrace_line + 1; const lbrace_col = func.lbrace_column + 1; const debug_parameter = try o.builder.debugParameter( - try o.builder.metadataString(mod.getParamName(func_index, src_index)), + try o.builder.metadataString(self.air.nullTerminatedString(@intFromEnum(name))), self.file, self.scope, lbrace_line, @@ -9664,7 +9659,7 @@ pub const FuncGen = struct { defer wip_switch.finish(&self.wip); for (0..names.len) |name_index| { - const err_int = mod.global_error_set.getIndex(names.get(ip)[name_index]).?; + const err_int = ip.getErrorValueIfExists(names.get(ip)[name_index]).?; const this_tag_int_value = try o.builder.intConst(try o.errorIntType(), err_int); try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); } @@ -9702,18 +9697,19 @@ pub const FuncGen = struct { const o = self.dg.object; const pt = o.pt; const zcu = pt.zcu; - const enum_type = zcu.intern_pool.loadEnumType(enum_ty.toIntern()); + const ip = &zcu.intern_pool; + const enum_type = ip.loadEnumType(enum_ty.toIntern()); // TODO: detect when the type changes and re-emit this function. const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_type.decl)); - const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(pt); + const decl = zcu.declPtr(enum_type.decl); const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(.i1, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.strtabStringFmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&zcu.intern_pool)}), + try o.builder.strtabStringFmt("__zig_is_named_enum_value_{}", .{decl.fqn.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 92cff8b2d0..7a45429da6 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -963,7 +963,7 @@ const DeclGen = struct { break :cache result_id; }, .err => |err| { - const value = try mod.getErrorValue(err.name); + const value = try pt.getErrorValue(err.name); break :cache try self.constInt(ty, value, repr); }, .error_union => |error_union| { @@ -3012,12 +3012,11 @@ const DeclGen = struct { // Append the actual code into the functions section. try self.spv.addFunction(spv_decl_index, self.func); - const fqn = try decl.fullyQualifiedName(self.pt); - try self.spv.debugName(result_id, fqn.toSlice(ip)); + try self.spv.debugName(result_id, decl.fqn.toSlice(ip)); // Temporarily generate a test kernel declaration if this is a test function. if (self.pt.zcu.test_functions.contains(self.decl_index)) { - try self.generateTestEntryPoint(fqn.toSlice(ip), spv_decl_index); + try self.generateTestEntryPoint(decl.fqn.toSlice(ip), spv_decl_index); } }, .global => { @@ -3041,8 +3040,7 @@ const DeclGen = struct { .storage_class = final_storage_class, }); - const fqn = try decl.fullyQualifiedName(self.pt); - try self.spv.debugName(result_id, fqn.toSlice(ip)); + try self.spv.debugName(result_id, decl.fqn.toSlice(ip)); try self.spv.declareDeclDeps(spv_decl_index, &.{}); }, .invocation_global => { @@ -3086,8 +3084,7 @@ const DeclGen = struct { try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); try self.spv.addFunction(spv_decl_index, self.func); - const fqn = try decl.fullyQualifiedName(self.pt); - try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{fqn.fmt(ip)}); + try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{decl.fqn.fmt(ip)}); try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{ .id_result_type = ptr_ty_id, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index bd1c96bf8b..84d26b7610 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1176,9 +1176,10 @@ pub fn lowerUnnamedConst(self: *Coff, pt: Zcu.PerThread, val: Value, decl_index: gop.value_ptr.* = .{}; } const unnamed_consts = gop.value_ptr; - const decl_name = try decl.fullyQualifiedName(pt); const index = unnamed_consts.items.len; - const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); + const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ + decl.fqn.fmt(&mod.intern_pool), index, + }); defer gpa.free(sym_name); const ty = val.typeOf(mod); const atom_index = switch (try self.lowerConst(pt, sym_name, val, ty.abiAlignment(pt), self.rdata_section_index.?, decl.navSrcLoc(mod))) { @@ -1427,9 +1428,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd const mod = pt.zcu; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - - log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); + log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(&mod.intern_pool), decl }); const required_alignment: u32 = @intCast(decl.getAlignment(pt).toByteUnits() orelse 0); const decl_metadata = self.decls.get(decl_index).?; @@ -1441,7 +1440,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd if (atom.size != 0) { const sym = atom.getSymbolPtr(self); - try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool)); + try self.setSymbolName(sym, decl.fqn.toSlice(&mod.intern_pool)); sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1)); sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; @@ -1449,7 +1448,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); if (need_realloc) { const vaddr = try self.growAtom(atom_index, code_len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), sym.value, vaddr }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(&mod.intern_pool), sym.value, vaddr }); log.debug(" (required alignment 0x{x}", .{required_alignment}); if (vaddr != sym.value) { @@ -1465,13 +1464,13 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd self.getAtomPtr(atom_index).size = code_len; } else { const sym = atom.getSymbolPtr(self); - try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool)); + try self.setSymbolName(sym, decl.fqn.toSlice(&mod.intern_pool)); sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1)); sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); errdefer self.freeAtom(atom_index); - log.debug("allocated atom for {} at 0x{x}", .{ decl_name.fmt(&mod.intern_pool), vaddr }); + log.debug("allocated atom for {} at 0x{x}", .{ decl.fqn.fmt(&mod.intern_pool), vaddr }); self.getAtomPtr(atom_index).size = code_len; sym.value = vaddr; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 9ae4ee3be6..9f2781549c 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1082,9 +1082,7 @@ pub fn initDeclState(self: *Dwarf, pt: Zcu.PerThread, decl_index: InternPool.Dec defer tracy.end(); const decl = pt.zcu.declPtr(decl_index); - const decl_linkage_name = try decl.fullyQualifiedName(pt); - - log.debug("initDeclState {}{*}", .{ decl_linkage_name.fmt(&pt.zcu.intern_pool), decl }); + log.debug("initDeclState {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl }); const gpa = self.allocator; var decl_state: DeclState = .{ @@ -1157,7 +1155,7 @@ pub fn initDeclState(self: *Dwarf, pt: Zcu.PerThread, decl_index: InternPool.Dec // .debug_info subprogram const decl_name_slice = decl.name.toSlice(&pt.zcu.intern_pool); - const decl_linkage_name_slice = decl_linkage_name.toSlice(&pt.zcu.intern_pool); + const decl_linkage_name_slice = decl.fqn.toSlice(&pt.zcu.intern_pool); try dbg_info_buffer.ensureUnusedCapacity(1 + ptr_width_bytes + 4 + 4 + (decl_name_slice.len + 1) + (decl_linkage_name_slice.len + 1)); @@ -2700,7 +2698,7 @@ pub fn flushModule(self: *Dwarf, pt: Zcu.PerThread) !void { try addDbgInfoErrorSetNames( pt, Type.anyerror, - pt.zcu.global_error_set.keys(), + pt.zcu.intern_pool.global_error_set.getNamesFromMainThread(), target, &dbg_info_buffer, ); @@ -2869,7 +2867,7 @@ fn addDbgInfoErrorSetNames( mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); for (error_names) |error_name| { - const int = try pt.zcu.getErrorValue(error_name); + const int = try pt.getErrorValue(error_name); const error_name_slice = error_name.toSlice(&pt.zcu.intern_pool); // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(error_name_slice.len + 2 + @sizeOf(u64)); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 7a419750d4..46c0fd23a3 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -907,10 +907,10 @@ fn updateDeclCode( ) !void { const gpa = elf_file.base.comp.gpa; const mod = pt.zcu; + const ip = &mod.intern_pool; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); + log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(ip), decl }); const required_alignment = decl.getAlignment(pt).max( target_util.minFunctionAlignment(mod.getTarget()), @@ -923,7 +923,7 @@ fn updateDeclCode( sym.output_section_index = shdr_index; atom_ptr.output_section_index = shdr_index; - sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); + sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; @@ -940,7 +940,7 @@ fn updateDeclCode( const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value)); if (need_realloc) { try atom_ptr.grow(elf_file); - log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom_ptr.value }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(ip), old_vaddr, atom_ptr.value }); if (old_vaddr != atom_ptr.value) { sym.value = 0; esym.st_value = 0; @@ -1007,11 +1007,11 @@ fn updateTlv( code: []const u8, ) !void { const mod = pt.zcu; + const ip = &mod.intern_pool; const gpa = mod.gpa; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&mod.intern_pool), decl }); + log.debug("updateTlv {} ({*})", .{ decl.fqn.fmt(ip), decl }); const required_alignment = decl.getAlignment(pt); @@ -1023,7 +1023,7 @@ fn updateTlv( sym.output_section_index = shndx; atom_ptr.output_section_index = shndx; - sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); + sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_value = 0; @@ -1286,9 +1286,8 @@ pub fn lowerUnnamedConst( } const unnamed_consts = gop.value_ptr; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index }); defer gpa.free(name); const ty = val.typeOf(mod); const sym_index = switch (try self.lowerConst( @@ -1473,9 +1472,8 @@ pub fn updateDeclLineNumber( defer tracy.end(); const decl = pt.zcu.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&pt.zcu.intern_pool), decl }); + log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl }); if (self.dwarf) |*dw| { try dw.updateDeclLineNumber(pt.zcu, decl_index); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 03e659c497..79e1ae4e02 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -809,10 +809,10 @@ fn updateDeclCode( ) !void { const gpa = macho_file.base.comp.gpa; const mod = pt.zcu; + const ip = &mod.intern_pool; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); + log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(ip), decl }); const required_alignment = decl.getAlignment(pt); @@ -824,7 +824,7 @@ fn updateDeclCode( sym.out_n_sect = sect_index; atom.out_n_sect = sect_index; - sym.name = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); + sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); atom.flags.alive = true; atom.name = sym.name; nlist.n_strx = sym.name; @@ -843,7 +843,7 @@ fn updateDeclCode( if (need_realloc) { try atom.grow(macho_file); - log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom.value }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(ip), old_vaddr, atom.value }); if (old_vaddr != atom.value) { sym.value = 0; nlist.n_value = 0; @@ -893,25 +893,22 @@ fn updateTlv( sect_index: u8, code: []const u8, ) !void { + const ip = &pt.zcu.intern_pool; const decl = pt.zcu.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&pt.zcu.intern_pool), decl }); - - const decl_name_slice = decl_name.toSlice(&pt.zcu.intern_pool); - const required_alignment = decl.getAlignment(pt); + log.debug("updateTlv {} ({*})", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl }); // 1. Lower TLV initializer const init_sym_index = try self.createTlvInitializer( macho_file, - decl_name_slice, - required_alignment, + decl.fqn.toSlice(ip), + decl.getAlignment(pt), sect_index, code, ); // 2. Create TLV descriptor - try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name_slice); + try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl.fqn.toSlice(ip)); } fn createTlvInitializer( @@ -1099,9 +1096,8 @@ pub fn lowerUnnamedConst( } const unnamed_consts = gop.value_ptr; const decl = mod.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index }); defer gpa.free(name); const sym_index = switch (try self.lowerConst( macho_file, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index cfc8435906..091aee54c4 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -483,11 +483,9 @@ pub fn lowerUnnamedConst(self: *Plan9, pt: Zcu.PerThread, val: Value, decl_index } const unnamed_consts = gop.value_ptr; - const decl_name = try decl.fullyQualifiedName(pt); - const index = unnamed_consts.items.len; // name is freed when the unnamed const is freed - const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index }); const sym_index = try self.allocateSymbolIndex(); const new_atom_idx = try self.createAtom(); diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 14020433bf..ce7e25824c 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -227,9 +227,9 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n var error_info = std.ArrayList(u8).init(self.object.gpa); defer error_info.deinit(); - try error_info.appendSlice("zig_errors"); - const mod = self.base.comp.module.?; - for (mod.global_error_set.keys()) |name| { + try error_info.appendSlice("zig_errors:"); + const ip = &self.base.comp.module.?.intern_pool; + for (ip.global_error_set.getNamesFromMainThread()) |name| { // Errors can contain pretty much any character - to encode them in a string we must escape // them somehow. Easiest here is to use some established scheme, one which also preseves the // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. @@ -238,7 +238,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n try error_info.append(':'); try std.Uri.Component.percentEncode( error_info.writer(), - name.toSlice(&mod.intern_pool), + name.toSlice(ip), struct { fn isValidChar(c: u8) bool { return switch (c) { diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index f95c8fc794..f74705e17c 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -346,8 +346,7 @@ fn finishUpdateDecl( const atom_index = decl_info.atom; const atom = wasm_file.getAtomPtr(atom_index); const sym = zig_object.symbol(atom.sym_index); - const full_name = try decl.fullyQualifiedName(pt); - sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(ip)); + sym.name = try zig_object.string_table.insert(gpa, decl.fqn.toSlice(ip)); try atom.code.appendSlice(gpa, code); atom.size = @intCast(code.len); @@ -387,7 +386,7 @@ fn finishUpdateDecl( // Will be freed upon freeing of decl or after cleanup of Wasm binary. const full_segment_name = try std.mem.concat(gpa, u8, &.{ segment_name, - full_name.toSlice(ip), + decl.fqn.toSlice(ip), }); errdefer gpa.free(full_segment_name); sym.tag = .data; @@ -436,9 +435,8 @@ pub fn getOrCreateAtomForDecl( const sym_index = try zig_object.allocateSymbol(gpa); gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) }; const decl = pt.zcu.declPtr(decl_index); - const full_name = try decl.fullyQualifiedName(pt); const sym = zig_object.symbol(sym_index); - sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(&pt.zcu.intern_pool)); + sym.name = try zig_object.string_table.insert(gpa, decl.fqn.toSlice(&pt.zcu.intern_pool)); } return gop.value_ptr.atom; } @@ -494,9 +492,8 @@ pub fn lowerUnnamedConst( const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, pt, decl_index); const parent_atom = wasm_file.getAtom(parent_atom_index); const local_index = parent_atom.locals.items.len; - const fqn = try decl.fullyQualifiedName(pt); const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{}_{d}", .{ - fqn.fmt(&mod.intern_pool), local_index, + decl.fqn.fmt(&mod.intern_pool), local_index, }); defer gpa.free(name); @@ -655,13 +652,22 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per // Addend for each relocation to the table var addend: u32 = 0; const pt: Zcu.PerThread = .{ .zcu = wasm_file.base.comp.module.?, .tid = tid }; - for (pt.zcu.global_error_set.keys()) |error_name| { - const atom = wasm_file.getAtomPtr(atom_index); + const slice_ty = Type.slice_const_u8_sentinel_0; + const atom = wasm_file.getAtomPtr(atom_index); + { + // TODO: remove this unreachable entry + try atom.code.appendNTimes(gpa, 0, 4); + try atom.code.writer(gpa).writeInt(u32, 0, .little); + atom.size += @intCast(slice_ty.abiSize(pt)); + addend += 1; - const error_name_slice = error_name.toSlice(&pt.zcu.intern_pool); + try names_atom.code.append(gpa, 0); + } + const ip = &pt.zcu.intern_pool; + for (ip.global_error_set.getNamesFromMainThread()) |error_name| { + const error_name_slice = error_name.toSlice(ip); const len: u32 = @intCast(error_name_slice.len + 1); // names are 0-terminated - const slice_ty = Type.slice_const_u8_sentinel_0; const offset = @as(u32, @intCast(atom.code.items.len)); // first we create the data for the slice of the name try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated @@ -680,7 +686,7 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per try names_atom.code.ensureUnusedCapacity(gpa, len); names_atom.code.appendSliceAssumeCapacity(error_name_slice[0..len]); - log.debug("Populated error name: '{}'", .{error_name.fmt(&pt.zcu.intern_pool)}); + log.debug("Populated error name: '{}'", .{error_name.fmt(ip)}); } names_atom.size = addend; zig_object.error_names_atom = names_atom_index; @@ -1045,7 +1051,7 @@ fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void { const gpa = wasm_file.base.comp.gpa; const sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return; - const errors_len = wasm_file.base.comp.module.?.global_error_set.count(); + const errors_len = 1 + wasm_file.base.comp.module.?.intern_pool.global_error_set.mutate.list.len; // overwrite existing atom if it already exists (maybe the error set has increased) // if not, allcoate a new atom. const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: { @@ -1127,9 +1133,7 @@ pub fn updateDeclLineNumber( ) !void { if (zig_object.dwarf) |*dw| { const decl = pt.zcu.declPtr(decl_index); - const decl_name = try decl.fullyQualifiedName(pt); - - log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&pt.zcu.intern_pool), decl }); + log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl }); try dw.updateDeclLineNumber(pt.zcu, decl_index); } } diff --git a/src/print_air.zig b/src/print_air.zig index d85750bd27..1872b480f5 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -356,7 +356,13 @@ const Writer = struct { fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg; try w.writeType(s, arg.ty.toType()); - try s.print(", {d}", .{arg.src_index}); + switch (arg.name) { + .none => {}, + _ => { + const name = w.air.nullTerminatedString(@intFromEnum(arg.name)); + try s.print(", \"{}\"", .{std.zig.fmtEscapes(name)}); + }, + } } fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/print_value.zig b/src/print_value.zig index 19e70d0564..c19ba43af3 100644 --- a/src/print_value.zig +++ b/src/print_value.zig @@ -299,8 +299,8 @@ fn printPtrDerivation(derivation: Value.PointerDeriveStep, writer: anytype, leve int.ptr_ty.fmt(pt), int.addr, }), - .decl_ptr => |decl| { - try zcu.declPtr(decl).renderFullyQualifiedName(zcu, writer); + .decl_ptr => |decl_index| { + try writer.print("{}", .{zcu.declPtr(decl_index).fqn.fmt(ip)}); }, .anon_decl_ptr => |anon| { const ty = Value.fromInterned(anon.val).typeOf(zcu);