authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-03 22:37:09-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-07 22:59:52-04:00
log8293ff94cf2798a2678b91019979472d34273bdb
tree8ec93b2e00f8bfd0e7fc477b09180dc910230fa3
parent3e1b190fe6955ba051d961494433b8346af2af38

InternPool: implement and use thread-safe list for strings


6 files changed, 380 insertions(+), 157 deletions(-)

lib/std/multi_array_list.zig+1-1
...@@ -534,7 +534,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -534,7 +534,7 @@ pub fn MultiArrayList(comptime T: type) type {
534 self.sortInternal(a, b, ctx, .unstable);534 self.sortInternal(a, b, ctx, .unstable);
535 }535 }
536536
537 fn capacityInBytes(capacity: usize) usize {537 pub fn capacityInBytes(capacity: usize) usize {
538 comptime var elem_bytes: usize = 0;538 comptime var elem_bytes: usize = 0;
539 inline for (sizes.bytes) |size| elem_bytes += size;539 inline for (sizes.bytes) |size| elem_bytes += size;
540 return elem_bytes * capacity;540 return elem_bytes * capacity;
src/Compilation.zig+3-3
...@@ -2748,7 +2748,7 @@ const Header = extern struct {...@@ -2748,7 +2748,7 @@ const Header = extern struct {
2748 items_len: u32,2748 items_len: u32,
2749 extra_len: u32,2749 extra_len: u32,
2750 limbs_len: u32,2750 limbs_len: u32,
2751 string_bytes_len: u32,2751 //string_bytes_len: u32,
2752 tracked_insts_len: u32,2752 tracked_insts_len: u32,
2753 src_hash_deps_len: u32,2753 src_hash_deps_len: u32,
2754 decl_val_deps_len: u32,2754 decl_val_deps_len: u32,
...@@ -2777,7 +2777,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -2777,7 +2777,7 @@ pub fn saveState(comp: *Compilation) !void {
2777 .items_len = @intCast(ip.items.len),2777 .items_len = @intCast(ip.items.len),
2778 .extra_len = @intCast(ip.extra.items.len),2778 .extra_len = @intCast(ip.extra.items.len),
2779 .limbs_len = @intCast(ip.limbs.items.len),2779 .limbs_len = @intCast(ip.limbs.items.len),
2780 .string_bytes_len = @intCast(ip.string_bytes.items.len),2780 //.string_bytes_len = @intCast(ip.string_bytes.items.len),
2781 .tracked_insts_len = @intCast(ip.tracked_insts.count()),2781 .tracked_insts_len = @intCast(ip.tracked_insts.count()),
2782 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),2782 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
2783 .decl_val_deps_len = @intCast(ip.decl_val_deps.count()),2783 .decl_val_deps_len = @intCast(ip.decl_val_deps.count()),
...@@ -2794,7 +2794,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -2794,7 +2794,7 @@ pub fn saveState(comp: *Compilation) !void {
2794 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.extra.items));2794 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.extra.items));
2795 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.data)));2795 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.data)));
2796 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.tag)));2796 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.tag)));
2797 addBuf(&bufs_list, &bufs_len, ip.string_bytes.items);2797 //addBuf(&bufs_list, &bufs_len, ip.string_bytes.items);
2798 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys()));2798 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys()));
27992799
2800 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.keys()));2800 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.keys()));
src/InternPool.zig+335-125
...@@ -2,9 +2,11 @@...@@ -2,9 +2,11 @@
2//! This data structure is self-contained, with the following exceptions:2//! This data structure is self-contained, with the following exceptions:
3//! * Module.Namespace has a pointer to Module.File3//! * Module.Namespace has a pointer to Module.File
44
5local: []Local = &.{},5locals: []Local = &.{},
6shard_shift: std.math.Log2Int(usize) = 0,
7shards: []Shard = &.{},6shards: []Shard = &.{},
7tid_width: std.math.Log2Int(u32) = 0,
8tid_shift_31: std.math.Log2Int(u32) = 31,
9tid_shift_32: std.math.Log2Int(u32) = 31,
810
9items: std.MultiArrayList(Item) = .{},11items: std.MultiArrayList(Item) = .{},
10extra: std.ArrayListUnmanaged(u32) = .{},12extra: std.ArrayListUnmanaged(u32) = .{},
...@@ -13,12 +15,6 @@ extra: std.ArrayListUnmanaged(u32) = .{},...@@ -13,12 +15,6 @@ extra: std.ArrayListUnmanaged(u32) = .{},
13/// Use the helper methods instead of accessing this directly in order to not15/// Use the helper methods instead of accessing this directly in order to not
14/// violate the above mechanism.16/// violate the above mechanism.
15limbs: std.ArrayListUnmanaged(u64) = .{},17limbs: std.ArrayListUnmanaged(u64) = .{},
16/// In order to store references to strings in fewer bytes, we copy all
17/// string bytes into here. String bytes can be null. It is up to whomever
18/// is referencing the data here whether they want to store both index and length,
19/// thus allowing null bytes, or store only index, and use null-termination. The
20/// `string_bytes` array is agnostic to either usage.
21string_bytes: std.ArrayListUnmanaged(u8) = .{},
2218
23/// Rather than allocating Decl objects with an Allocator, we instead allocate19/// Rather than allocating Decl objects with an Allocator, we instead allocate
24/// them with this SegmentedList. This provides four advantages:20/// them with this SegmentedList. This provides four advantages:
...@@ -345,52 +341,237 @@ pub const DepEntry = extern struct {...@@ -345,52 +341,237 @@ pub const DepEntry = extern struct {
345};341};
346342
347const Local = struct {343const Local = struct {
348 aligned: void align(std.atomic.cache_line) = {},344 shared: Shared align(std.atomic.cache_line),
345 mutate: struct {
346 arena: std.heap.ArenaAllocator.State,
347 strings: Mutate,
348 } align(std.atomic.cache_line),
349349
350 /// header: List.Header,350 const Shared = struct {
351 /// data: [capacity]u32,351 strings: Strings,
352 /// tag: [header.capacity]Tag,352 };
353 items: List,
354353
355 /// header: List.Header,354 const Strings = List(struct { u8 });
356 /// extra: [header.capacity]u32,
357 extra: List,
358355
359 /// header: List.Header,356 const Mutate = struct {
360 /// bytes: [header.capacity]u8,357 len: u32,
361 strings: List,
362358
363 arena: std.heap.ArenaAllocator.State,359 const empty: Mutate = .{
360 .len = 0,
361 };
362 };
364363
365 const List = struct {364 fn List(comptime Elem: type) type {
366 entries: [*]u32,365 assert(@typeInfo(Elem) == .Struct);
366 return struct {
367 bytes: [*]align(@alignOf(Elem)) u8,
368
369 const ListSelf = @This();
370 const Mutable = struct {
371 gpa: std.mem.Allocator,
372 arena: *std.heap.ArenaAllocator.State,
373 mutate: *Mutate,
374 list: *ListSelf,
375
376 const fields = std.enums.values(std.meta.FieldEnum(Elem));
377
378 fn Slice(comptime opts: struct { is_const: bool = false }) type {
379 const elem_info = @typeInfo(Elem).Struct;
380 const elem_fields = elem_info.fields;
381 var new_fields: [elem_fields.len]std.builtin.Type.StructField = undefined;
382 for (&new_fields, elem_fields) |*new_field, elem_field| new_field.* = .{
383 .name = elem_field.name,
384 .type = @Type(.{ .Pointer = .{
385 .size = .Slice,
386 .is_const = opts.is_const,
387 .is_volatile = false,
388 .alignment = 0,
389 .address_space = .generic,
390 .child = elem_field.type,
391 .is_allowzero = false,
392 .sentinel = null,
393 } }),
394 .default_value = null,
395 .is_comptime = false,
396 .alignment = 0,
397 };
398 return @Type(.{ .Struct = .{
399 .layout = .auto,
400 .fields = &new_fields,
401 .decls = &.{},
402 .is_tuple = elem_info.is_tuple,
403 } });
404 }
367405
368 const empty: List = .{ .entries = @constCast(&(extern struct {406 pub fn appendAssumeCapacity(mutable: Mutable, elem: Elem) void {
369 header: Header,407 var mutable_view = mutable.view();
370 entries: [0]u32,408 defer mutable.lenPtr().* = @intCast(mutable_view.len);
371 }{409 mutable_view.appendAssumeCapacity(elem);
372 .header = .{ .len = 0, .capacity = 0 },410 }
373 .entries = .{},
374 }).entries) };
375411
376 fn acquire(list: *const List) List {412 pub fn appendSliceAssumeCapacity(
377 return .{ .entries = @atomicLoad([*]u32, &list.entries, .acquire) };413 mutable: Mutable,
378 }414 slice: Slice(.{ .is_const = true }),
379 fn release(list: *List, new_list: List) void {415 ) void {
380 @atomicStore([*]u32, &list.entries, new_list.entries, .release);416 if (fields.len == 0) return;
381 }417 const mutable_len = mutable.lenPtr();
418 const start = mutable_len.*;
419 const slice_len = @field(slice, @tagName(fields[0])).len;
420 assert(slice_len < mutable.capacityPtr().* - start);
421 mutable_len.* = @intCast(start + slice_len);
422 const mutable_view = mutable.view();
423 inline for (fields) |field| {
424 const field_slice = @field(slice, @tagName(field));
425 assert(field_slice.len == slice_len);
426 @memcpy(mutable_view.items(field)[start..][0..slice_len], field_slice);
427 }
428 }
382429
383 const Header = extern struct {430 pub fn appendNTimes(mutable: Mutable, elem: Elem, len: usize) Allocator.Error!void {
384 len: u32,431 try mutable.ensureUnusedCapacity(len);
385 capacity: u32,432 mutable.appendNTimesAssumeCapacity(elem, len);
433 }
434
435 pub fn appendNTimesAssumeCapacity(mutable: Mutable, elem: Elem, len: usize) void {
436 const mutable_len = mutable.lenPtr();
437 const start = mutable_len.*;
438 assert(len <= mutable.capacityPtr().* - start);
439 mutable_len.* = @intCast(start + len);
440 const mutable_view = mutable.view();
441 inline for (fields) |field| {
442 @memset(mutable_view.items(field)[start..][0..len], @field(elem, @tagName(field)));
443 }
444 }
445
446 pub fn addManyAsSlice(mutable: Mutable, len: usize) Allocator.Error!Slice(.{}) {
447 try mutable.ensureUnusedCapacity(len);
448 return mutable.addManyAsSliceAssumeCapacity(len);
449 }
450
451 pub fn addManyAsSliceAssumeCapacity(mutable: Mutable, len: usize) Slice(.{}) {
452 const mutable_len = mutable.lenPtr();
453 const start = mutable_len.*;
454 assert(len <= mutable.capacityPtr().* - start);
455 mutable_len.* = @intCast(start + len);
456 const mutable_view = mutable.view();
457 var slice: Slice(.{}) = undefined;
458 inline for (fields) |field| {
459 @field(slice, @tagName(field)) = mutable_view.items(field)[start..][0..len];
460 }
461 return slice;
462 }
463
464 pub fn shrinkRetainingCapacity(mutable: Mutable, len: usize) void {
465 const mutable_len = mutable.lenPtr();
466 assert(len <= mutable_len.*);
467 mutable_len.* = @intCast(len);
468 }
469
470 pub fn ensureUnusedCapacity(mutable: Mutable, unused_capacity: usize) Allocator.Error!void {
471 try mutable.ensureTotalCapacity(@intCast(mutable.lenPtr().* + unused_capacity));
472 }
473
474 pub fn ensureTotalCapacity(mutable: Mutable, total_capacity: usize) Allocator.Error!void {
475 const old_capacity = mutable.capacityPtr().*;
476 if (old_capacity >= total_capacity) return;
477 var new_capacity = old_capacity;
478 while (new_capacity < total_capacity) new_capacity = (new_capacity + 10) * 2;
479 try mutable.setCapacity(new_capacity);
480 }
481
482 fn setCapacity(mutable: Mutable, capacity: u32) Allocator.Error!void {
483 var arena = mutable.arena.promote(mutable.gpa);
484 defer mutable.arena.* = arena.state;
485 const buf = try arena.allocator().alignedAlloc(
486 u8,
487 alignment,
488 bytes_offset + View.capacityInBytes(capacity),
489 );
490 var new_list: ListSelf = .{ .bytes = @ptrCast(buf[bytes_offset..].ptr) };
491 new_list.header().* = .{ .capacity = capacity };
492 const len = mutable.lenPtr().*;
493 const old_slice = mutable.list.view().slice();
494 const new_slice = new_list.view().slice();
495 inline for (fields) |field| {
496 @memcpy(new_slice.items(field)[0..len], old_slice.items(field)[0..len]);
497 }
498 mutable.list.release(new_list);
499 }
500
501 fn view(mutable: Mutable) View {
502 return .{
503 .bytes = mutable.list.bytes,
504 .len = mutable.lenPtr().*,
505 .capacity = mutable.capacityPtr().*,
506 };
507 }
508
509 pub fn lenPtr(mutable: Mutable) *u32 {
510 return &mutable.mutate.len;
511 }
512
513 pub fn capacityPtr(mutable: Mutable) *u32 {
514 return &mutable.list.header().capacity;
515 }
516 };
517
518 const empty: ListSelf = .{ .bytes = @constCast(&(extern struct {
519 header: Header,
520 bytes: [0]u8,
521 }{
522 .header = .{ .capacity = 0 },
523 .bytes = .{},
524 }).bytes) };
386525
387 const fields_len = @typeInfo(Header).Struct.fields.len;526 const alignment = @max(@alignOf(Header), @alignOf(Elem));
527 const bytes_offset = std.mem.alignForward(usize, @sizeOf(Header), @alignOf(Elem));
528 const View = std.MultiArrayList(Elem);
529
530 fn acquire(list: *const ListSelf) ListSelf {
531 return .{ .bytes = @atomicLoad([*]align(@alignOf(Elem)) u8, &list.bytes, .acquire) };
532 }
533 fn release(list: *ListSelf, new_list: ListSelf) void {
534 @atomicStore([*]align(@alignOf(Elem)) u8, &list.bytes, new_list.bytes, .release);
535 }
536
537 const Header = extern struct {
538 capacity: u32,
539 };
540 fn header(list: ListSelf) *Header {
541 return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset);
542 }
543
544 fn view(list: ListSelf) View {
545 const capacity = list.header().capacity;
546 return .{
547 .bytes = list.bytes,
548 .len = capacity,
549 .capacity = capacity,
550 };
551 }
388 };552 };
389 fn header(list: List) *Header {553 }
390 return @ptrCast(list.entries - Header.fields_len);554
391 }555 /// In order to store references to strings in fewer bytes, we copy all
392 };556 /// string bytes into here. String bytes can be null. It is up to whomever
557 /// is referencing the data here whether they want to store both index and length,
558 /// thus allowing null bytes, or store only index, and use null-termination. The
559 /// `strings` array is agnostic to either usage.
560 pub fn getMutableStrings(local: *Local, gpa: std.mem.Allocator) Strings.Mutable {
561 return .{
562 .gpa = gpa,
563 .arena = &local.mutate.arena,
564 .mutate = &local.mutate.strings,
565 .list = &local.shared.strings,
566 };
567 }
393};568};
569pub fn getLocal(ip: *InternPool, tid: Zcu.PerThread.Id) *Local {
570 return &ip.locals[@intFromEnum(tid)];
571}
572pub fn getLocalShared(ip: *const InternPool, tid: Zcu.PerThread.Id) *const Local.Shared {
573 return &ip.locals[@intFromEnum(tid)].shared;
574}
394575
395const Shard = struct {576const Shard = struct {
396 shared: struct {577 shared: struct {
...@@ -448,7 +629,7 @@ const Shard = struct {...@@ -448,7 +629,7 @@ const Shard = struct {
448 }629 }
449 };630 };
450 fn header(map: @This()) *Header {631 fn header(map: @This()) *Header {
451 return &(@as([*]Header, @ptrCast(map.entries)) - 1)[0];632 return @ptrFromInt(@intFromPtr(map.entries) - entries_offset);
452 }633 }
453634
454 const Entry = extern struct {635 const Entry = extern struct {
...@@ -465,6 +646,17 @@ const Shard = struct {...@@ -465,6 +646,17 @@ const Shard = struct {
465 };646 };
466 }647 }
467};648};
649fn getShard(ip: *InternPool, tid: Zcu.PerThread.Id) *Shard {
650 return &ip.shards[@intFromEnum(tid)];
651}
652
653fn getTidMask(ip: *const InternPool) u32 {
654 assert(std.math.isPowerOfTwo(ip.shards.len));
655 return @intCast(ip.shards.len - 1);
656}
657fn getIndexMask(ip: *const InternPool, comptime BackingInt: type) u32 {
658 return @as(u32, std.math.maxInt(BackingInt)) >> ip.tid_width;
659}
468660
469const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false);661const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false);
470662
...@@ -560,18 +752,18 @@ pub const OptionalNamespaceIndex = enum(u32) {...@@ -560,18 +752,18 @@ pub const OptionalNamespaceIndex = enum(u32) {
560 }752 }
561};753};
562754
563/// An index into `string_bytes`.755/// An index into `strings`.
564pub const String = enum(u32) {756pub const String = enum(u32) {
565 /// An empty string.757 /// An empty string.
566 empty = 0,758 empty = 0,
567 _,759 _,
568760
569 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {761 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {
570 return ip.string_bytes.items[@intFromEnum(string)..][0..@intCast(len)];762 return string.toOverlongSlice(ip)[0..@intCast(len)];
571 }763 }
572764
573 pub fn at(string: String, index: u64, ip: *const InternPool) u8 {765 pub fn at(string: String, index: u64, ip: *const InternPool) u8 {
574 return ip.string_bytes.items[@intCast(@intFromEnum(string) + index)];766 return string.toOverlongSlice(ip)[@intCast(index)];
575 }767 }
576768
577 pub fn toNullTerminatedString(string: String, len: u64, ip: *const InternPool) NullTerminatedString {769 pub fn toNullTerminatedString(string: String, len: u64, ip: *const InternPool) NullTerminatedString {
...@@ -579,9 +771,32 @@ pub const String = enum(u32) {...@@ -579,9 +771,32 @@ pub const String = enum(u32) {
579 assert(string.at(len, ip) == 0);771 assert(string.at(len, ip) == 0);
580 return @enumFromInt(@intFromEnum(string));772 return @enumFromInt(@intFromEnum(string));
581 }773 }
774
775 const Unwrapped = struct {
776 tid: Zcu.PerThread.Id,
777 index: u32,
778
779 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) String {
780 assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask());
781 assert(unwrapped.index <= ip.getIndexMask(u32));
782 return @enumFromInt(@intFromEnum(unwrapped.tid) << ip.tid_shift_32 | unwrapped.index);
783 }
784 };
785 fn unwrap(string: String, ip: *const InternPool) Unwrapped {
786 return .{
787 .tid = @enumFromInt(@intFromEnum(string) >> ip.tid_shift_32 & ip.getTidMask()),
788 .index = @intFromEnum(string) & ip.getIndexMask(u32),
789 };
790 }
791
792 fn toOverlongSlice(string: String, ip: *const InternPool) []const u8 {
793 const unwrapped = string.unwrap(ip);
794 const strings = ip.getLocalShared(unwrapped.tid).strings.acquire();
795 return strings.view().items(.@"0")[unwrapped.index..];
796 }
582};797};
583798
584/// An index into `string_bytes` which might be `none`.799/// An index into `strings` which might be `none`.
585pub const OptionalString = enum(u32) {800pub const OptionalString = enum(u32) {
586 /// This is distinct from `none` - it is a valid index that represents empty string.801 /// This is distinct from `none` - it is a valid index that represents empty string.
587 empty = 0,802 empty = 0,
...@@ -597,7 +812,7 @@ pub const OptionalString = enum(u32) {...@@ -597,7 +812,7 @@ pub const OptionalString = enum(u32) {
597 }812 }
598};813};
599814
600/// An index into `string_bytes`.815/// An index into `strings`.
601pub const NullTerminatedString = enum(u32) {816pub const NullTerminatedString = enum(u32) {
602 /// An empty string.817 /// An empty string.
603 empty = 0,818 empty = 0,
...@@ -623,12 +838,8 @@ pub const NullTerminatedString = enum(u32) {...@@ -623,12 +838,8 @@ pub const NullTerminatedString = enum(u32) {
623 return @enumFromInt(@intFromEnum(self));838 return @enumFromInt(@intFromEnum(self));
624 }839 }
625840
626 fn toOverlongSlice(string: NullTerminatedString, ip: *const InternPool) []const u8 {
627 return ip.string_bytes.items[@intFromEnum(string)..];
628 }
629
630 pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 {841 pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 {
631 const overlong_slice = string.toOverlongSlice(ip);842 const overlong_slice = string.toString().toOverlongSlice(ip);
632 return overlong_slice[0..std.mem.indexOfScalar(u8, overlong_slice, 0).? :0];843 return overlong_slice[0..std.mem.indexOfScalar(u8, overlong_slice, 0).? :0];
633 }844 }
634845
...@@ -637,7 +848,7 @@ pub const NullTerminatedString = enum(u32) {...@@ -637,7 +848,7 @@ pub const NullTerminatedString = enum(u32) {
637 }848 }
638849
639 pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool {850 pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool {
640 const overlong_slice = string.toOverlongSlice(ip);851 const overlong_slice = string.toString().toOverlongSlice(ip);
641 return overlong_slice.len > slice.len and852 return overlong_slice.len > slice.len and
642 std.mem.eql(u8, overlong_slice[0..slice.len], slice) and853 std.mem.eql(u8, overlong_slice[0..slice.len], slice) and
643 overlong_slice[slice.len] == 0;854 overlong_slice[slice.len] == 0;
...@@ -688,12 +899,12 @@ pub const NullTerminatedString = enum(u32) {...@@ -688,12 +899,12 @@ pub const NullTerminatedString = enum(u32) {
688 } else @compileError("invalid format string '" ++ specifier ++ "' for '" ++ @typeName(NullTerminatedString) ++ "'");899 } else @compileError("invalid format string '" ++ specifier ++ "' for '" ++ @typeName(NullTerminatedString) ++ "'");
689 }900 }
690901
691 pub fn fmt(self: NullTerminatedString, ip: *const InternPool) std.fmt.Formatter(format) {902 pub fn fmt(string: NullTerminatedString, ip: *const InternPool) std.fmt.Formatter(format) {
692 return .{ .data = .{ .string = self, .ip = ip } };903 return .{ .data = .{ .string = string, .ip = ip } };
693 }904 }
694};905};
695906
696/// An index into `string_bytes` which might be `none`.907/// An index into `strings` which might be `none`.
697pub const OptionalNullTerminatedString = enum(u32) {908pub const OptionalNullTerminatedString = enum(u32) {
698 /// This is distinct from `none` - it is a valid index that represents empty string.909 /// This is distinct from `none` - it is a valid index that represents empty string.
699 empty = 0,910 empty = 0,
...@@ -4077,7 +4288,7 @@ pub const FuncAnalysis = packed struct(u32) {...@@ -4077,7 +4288,7 @@ pub const FuncAnalysis = packed struct(u32) {
4077pub const Bytes = struct {4288pub const Bytes = struct {
4078 /// The type of the aggregate4289 /// The type of the aggregate
4079 ty: Index,4290 ty: Index,
4080 /// Index into string_bytes, of len ip.aggregateTypeLen(ty)4291 /// Index into strings, of len ip.aggregateTypeLen(ty)
4081 bytes: String,4292 bytes: String,
4082};4293};
40834294
...@@ -4647,16 +4858,21 @@ pub fn init(ip: *InternPool, gpa: Allocator, total_threads: usize) !void {...@@ -4647,16 +4858,21 @@ pub fn init(ip: *InternPool, gpa: Allocator, total_threads: usize) !void {
4647 errdefer ip.deinit(gpa);4858 errdefer ip.deinit(gpa);
4648 assert(ip.items.len == 0);4859 assert(ip.items.len == 0);
46494860
4650 ip.local = try gpa.alloc(Local, total_threads);4861 ip.locals = try gpa.alloc(Local, total_threads);
4651 @memset(ip.local, .{4862 @memset(ip.locals, .{
4652 .items = Local.List.empty,4863 .shared = .{
4653 .extra = Local.List.empty,4864 .strings = Local.Strings.empty,
4654 .strings = Local.List.empty,4865 },
4655 .arena = .{},4866 .mutate = .{
4867 .arena = .{},
4868 .strings = Local.Mutate.empty,
4869 },
4656 });4870 });
46574871
4658 ip.shard_shift = @intCast(std.math.log2_int_ceil(usize, total_threads));4872 ip.tid_width = @intCast(std.math.log2_int_ceil(usize, total_threads));
4659 ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.shard_shift);4873 ip.tid_shift_31 = 31 - ip.tid_width;
4874 ip.tid_shift_32 = ip.tid_shift_31 +| 1;
4875 ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.tid_width);
4660 @memset(ip.shards, .{4876 @memset(ip.shards, .{
4661 .shared = .{4877 .shared = .{
4662 .map = Shard.Map(Index).empty,4878 .map = Shard.Map(Index).empty,
...@@ -4705,7 +4921,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {...@@ -4705,7 +4921,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
4705 ip.items.deinit(gpa);4921 ip.items.deinit(gpa);
4706 ip.extra.deinit(gpa);4922 ip.extra.deinit(gpa);
4707 ip.limbs.deinit(gpa);4923 ip.limbs.deinit(gpa);
4708 ip.string_bytes.deinit(gpa);
47094924
4710 ip.decls_free_list.deinit(gpa);4925 ip.decls_free_list.deinit(gpa);
4711 ip.allocated_decls.deinit(gpa);4926 ip.allocated_decls.deinit(gpa);
...@@ -4732,8 +4947,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {...@@ -4732,8 +4947,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
4732 ip.files.deinit(gpa);4947 ip.files.deinit(gpa);
47334948
4734 gpa.free(ip.shards);4949 gpa.free(ip.shards);
4735 for (ip.local) |*local| local.arena.promote(gpa).deinit();4950 for (ip.locals) |*local| local.mutate.arena.promote(gpa).deinit();
4736 gpa.free(ip.local);4951 gpa.free(ip.locals);
47374952
4738 ip.* = undefined;4953 ip.* = undefined;
4739}4954}
...@@ -5437,8 +5652,9 @@ fn getOrPutKey(...@@ -5437,8 +5652,9 @@ fn getOrPutKey(
5437 }5652 }
5438 const map_header = map.header().*;5653 const map_header = map.header().*;
5439 if (shard.mutate.map.len >= map_header.capacity * 3 / 5) {5654 if (shard.mutate.map.len >= map_header.capacity * 3 / 5) {
5440 var arena = ip.local[@intFromEnum(tid)].arena.promote(gpa);5655 const arena_state = &ip.getLocal(tid).mutate.arena;
5441 defer ip.local[@intFromEnum(tid)].arena = arena.state;5656 var arena = arena_state.promote(gpa);
5657 defer arena_state.* = arena.state;
5442 const new_map_capacity = map_header.capacity * 2;5658 const new_map_capacity = map_header.capacity * 2;
5443 const new_map_buf = try arena.allocator().alignedAlloc(5659 const new_map_buf = try arena.allocator().alignedAlloc(
5444 u8,5660 u8,
...@@ -6194,33 +6410,32 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All...@@ -6194,33 +6410,32 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
6194 }6410 }
61956411
6196 if (child == .u8_type) bytes: {6412 if (child == .u8_type) bytes: {
6197 const string_bytes_index = ip.string_bytes.items.len;6413 const strings = ip.getLocal(tid).getMutableStrings(gpa);
6198 try ip.string_bytes.ensureUnusedCapacity(gpa, @intCast(len_including_sentinel + 1));6414 const start = strings.lenPtr().*;
6415 try strings.ensureUnusedCapacity(@intCast(len_including_sentinel + 1));
6199 try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len);6416 try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len);
6200 switch (aggregate.storage) {6417 switch (aggregate.storage) {
6201 .bytes => |bytes| ip.string_bytes.appendSliceAssumeCapacity(bytes.toSlice(len, ip)),6418 .bytes => |bytes| strings.appendSliceAssumeCapacity(.{bytes.toSlice(len, ip)}),
6202 .elems => |elems| for (elems[0..@intCast(len)]) |elem| switch (ip.indexToKey(elem)) {6419 .elems => |elems| for (elems[0..@intCast(len)]) |elem| switch (ip.indexToKey(elem)) {
6203 .undef => {6420 .undef => {
6204 ip.string_bytes.shrinkRetainingCapacity(string_bytes_index);6421 strings.shrinkRetainingCapacity(start);
6205 break :bytes;6422 break :bytes;
6206 },6423 },
6207 .int => |int| ip.string_bytes.appendAssumeCapacity(6424 .int => |int| strings.appendAssumeCapacity(.{@intCast(int.storage.u64)}),
6208 @intCast(int.storage.u64),
6209 ),
6210 else => unreachable,6425 else => unreachable,
6211 },6426 },
6212 .repeated_elem => |elem| switch (ip.indexToKey(elem)) {6427 .repeated_elem => |elem| switch (ip.indexToKey(elem)) {
6213 .undef => break :bytes,6428 .undef => break :bytes,
6214 .int => |int| @memset(6429 .int => |int| @memset(
6215 ip.string_bytes.addManyAsSliceAssumeCapacity(@intCast(len)),6430 strings.addManyAsSliceAssumeCapacity(@intCast(len))[0],
6216 @intCast(int.storage.u64),6431 @intCast(int.storage.u64),
6217 ),6432 ),
6218 else => unreachable,6433 else => unreachable,
6219 },6434 },
6220 }6435 }
6221 if (sentinel != .none) ip.string_bytes.appendAssumeCapacity(6436 if (sentinel != .none) strings.appendAssumeCapacity(.{
6222 @intCast(ip.indexToKey(sentinel).int.storage.u64),6437 @intCast(ip.indexToKey(sentinel).int.storage.u64),
6223 );6438 });
6224 const string = try ip.getOrPutTrailingString(6439 const string = try ip.getOrPutTrailingString(
6225 gpa,6440 gpa,
6226 tid,6441 tid,
...@@ -9050,10 +9265,11 @@ pub fn getOrPutString(...@@ -9050,10 +9265,11 @@ pub fn getOrPutString(
9050 slice: []const u8,9265 slice: []const u8,
9051 comptime embedded_nulls: EmbeddedNulls,9266 comptime embedded_nulls: EmbeddedNulls,
9052) Allocator.Error!embedded_nulls.StringType() {9267) Allocator.Error!embedded_nulls.StringType() {
9053 try ip.string_bytes.ensureUnusedCapacity(gpa, slice.len + 1);9268 const strings = ip.getLocal(tid).getMutableStrings(gpa);
9054 ip.string_bytes.appendSliceAssumeCapacity(slice);9269 try strings.ensureUnusedCapacity(slice.len + 1);
9055 ip.string_bytes.appendAssumeCapacity(0);9270 strings.appendSliceAssumeCapacity(.{slice});
9056 return ip.getOrPutTrailingString(gpa, tid, slice.len + 1, embedded_nulls);9271 strings.appendAssumeCapacity(.{0});
9272 return ip.getOrPutTrailingString(gpa, tid, @intCast(slice.len + 1), embedded_nulls);
9057}9273}
90589274
9059pub fn getOrPutStringFmt(9275pub fn getOrPutStringFmt(
...@@ -9064,11 +9280,12 @@ pub fn getOrPutStringFmt(...@@ -9064,11 +9280,12 @@ pub fn getOrPutStringFmt(
9064 args: anytype,9280 args: anytype,
9065 comptime embedded_nulls: EmbeddedNulls,9281 comptime embedded_nulls: EmbeddedNulls,
9066) Allocator.Error!embedded_nulls.StringType() {9282) Allocator.Error!embedded_nulls.StringType() {
9067 // ensure that references to string_bytes in args do not get invalidated9283 // ensure that references to strings in args do not get invalidated
9068 const len: usize = @intCast(std.fmt.count(format, args) + 1);9284 const format_z = format ++ .{0};
9069 try ip.string_bytes.ensureUnusedCapacity(gpa, len);9285 const len: u32 = @intCast(std.fmt.count(format_z, args));
9070 ip.string_bytes.writer(undefined).print(format, args) catch unreachable;9286 const strings = ip.getLocal(tid).getMutableStrings(gpa);
9071 ip.string_bytes.appendAssumeCapacity(0);9287 const slice = try strings.addManyAsSlice(len);
9288 assert((std.fmt.bufPrint(slice[0], format_z, args) catch unreachable).len == len);
9072 return ip.getOrPutTrailingString(gpa, tid, len, embedded_nulls);9289 return ip.getOrPutTrailingString(gpa, tid, len, embedded_nulls);
9073}9290}
90749291
...@@ -9083,47 +9300,33 @@ pub fn getOrPutStringOpt(...@@ -9083,47 +9300,33 @@ pub fn getOrPutStringOpt(
9083 return string.toOptional();9300 return string.toOptional();
9084}9301}
90859302
9086/// Uses the last len bytes of ip.string_bytes as the key.9303/// Uses the last len bytes of strings as the key.
9087pub fn getOrPutTrailingString(9304pub fn getOrPutTrailingString(
9088 ip: *InternPool,9305 ip: *InternPool,
9089 gpa: Allocator,9306 gpa: Allocator,
9090 tid: Zcu.PerThread.Id,9307 tid: Zcu.PerThread.Id,
9091 len: usize,9308 len: u32,
9092 comptime embedded_nulls: EmbeddedNulls,9309 comptime embedded_nulls: EmbeddedNulls,
9093) Allocator.Error!embedded_nulls.StringType() {9310) Allocator.Error!embedded_nulls.StringType() {
9094 const string_bytes = &ip.string_bytes;9311 const strings = ip.getLocal(tid).getMutableStrings(gpa);
9095 const str_index: u32 = @intCast(string_bytes.items.len - len);9312 const start: u32 = @intCast(strings.lenPtr().* - len);
9096 if (len > 0 and string_bytes.getLast() == 0) {9313 if (len > 0 and strings.view().items(.@"0")[strings.lenPtr().* - 1] == 0) {
9097 _ = string_bytes.pop();9314 strings.lenPtr().* -= 1;
9098 } else {9315 } else {
9099 try string_bytes.ensureUnusedCapacity(gpa, 1);9316 try strings.ensureUnusedCapacity(1);
9100 }9317 }
9101 const key: []const u8 = string_bytes.items[str_index..];9318 const key: []const u8 = strings.view().items(.@"0")[start..];
9319 const value: embedded_nulls.StringType() =
9320 @enumFromInt(@intFromEnum(tid) << ip.tid_shift_32 | start);
9102 const has_embedded_null = std.mem.indexOfScalar(u8, key, 0) != null;9321 const has_embedded_null = std.mem.indexOfScalar(u8, key, 0) != null;
9103 switch (embedded_nulls) {9322 switch (embedded_nulls) {
9104 .no_embedded_nulls => assert(!has_embedded_null),9323 .no_embedded_nulls => assert(!has_embedded_null),
9105 .maybe_embedded_nulls => if (has_embedded_null) {9324 .maybe_embedded_nulls => if (has_embedded_null) {
9106 string_bytes.appendAssumeCapacity(0);9325 strings.appendAssumeCapacity(.{0});
9107 return @enumFromInt(str_index);9326 return value;
9108 },9327 },
9109 }9328 }
9110 const maybe_existing_index = try ip.getOrPutStringValue(gpa, tid, key, @enumFromInt(str_index));
9111 if (maybe_existing_index.unwrap()) |existing_index| {
9112 string_bytes.shrinkRetainingCapacity(str_index);
9113 return @enumFromInt(@intFromEnum(existing_index));
9114 } else {
9115 string_bytes.appendAssumeCapacity(0);
9116 return @enumFromInt(str_index);
9117 }
9118}
91199329
9120fn getOrPutStringValue(
9121 ip: *InternPool,
9122 gpa: Allocator,
9123 tid: Zcu.PerThread.Id,
9124 key: []const u8,
9125 value: NullTerminatedString,
9126) Allocator.Error!OptionalNullTerminatedString {
9127 const full_hash = Hash.hash(0, key);9330 const full_hash = Hash.hash(0, key);
9128 const hash: u32 = @truncate(full_hash >> 32);9331 const hash: u32 = @truncate(full_hash >> 32);
9129 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];9332 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
...@@ -9136,7 +9339,9 @@ fn getOrPutStringValue(...@@ -9136,7 +9339,9 @@ fn getOrPutStringValue(
9136 const entry = &map.entries[map_index];9339 const entry = &map.entries[map_index];
9137 const index = entry.acquire().unwrap() orelse break;9340 const index = entry.acquire().unwrap() orelse break;
9138 if (entry.hash != hash) continue;9341 if (entry.hash != hash) continue;
9139 if (index.eqlSlice(key, ip)) return index.toOptional();9342 if (!index.eqlSlice(key, ip)) continue;
9343 strings.shrinkRetainingCapacity(start);
9344 return @enumFromInt(@intFromEnum(index));
9140 }9345 }
9141 shard.mutate.string_map.mutex.lock();9346 shard.mutate.string_map.mutex.lock();
9142 defer shard.mutate.string_map.mutex.unlock();9347 defer shard.mutate.string_map.mutex.unlock();
...@@ -9151,18 +9356,22 @@ fn getOrPutStringValue(...@@ -9151,18 +9356,22 @@ fn getOrPutStringValue(
9151 const entry = &map.entries[map_index];9356 const entry = &map.entries[map_index];
9152 const index = entry.acquire().unwrap() orelse break;9357 const index = entry.acquire().unwrap() orelse break;
9153 if (entry.hash != hash) continue;9358 if (entry.hash != hash) continue;
9154 if (index.eqlSlice(key, ip)) return index.toOptional();9359 if (!index.eqlSlice(key, ip)) continue;
9360 strings.shrinkRetainingCapacity(start);
9361 return @enumFromInt(@intFromEnum(index));
9155 }9362 }
9156 defer shard.mutate.string_map.len += 1;9363 defer shard.mutate.string_map.len += 1;
9157 const map_header = map.header().*;9364 const map_header = map.header().*;
9158 if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) {9365 if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) {
9159 const entry = &map.entries[map_index];9366 const entry = &map.entries[map_index];
9160 entry.hash = hash;9367 entry.hash = hash;
9161 entry.release(value.toOptional());9368 entry.release(@enumFromInt(@intFromEnum(value)));
9162 return .none;9369 strings.appendAssumeCapacity(.{0});
9370 return value;
9163 }9371 }
9164 var arena = ip.local[@intFromEnum(tid)].arena.promote(gpa);9372 const arena_state = &ip.getLocal(tid).mutate.arena;
9165 defer ip.local[@intFromEnum(tid)].arena = arena.state;9373 var arena = arena_state.promote(gpa);
9374 defer arena_state.* = arena.state;
9166 const new_map_capacity = map_header.capacity * 2;9375 const new_map_capacity = map_header.capacity * 2;
9167 const new_map_buf = try arena.allocator().alignedAlloc(9376 const new_map_buf = try arena.allocator().alignedAlloc(
9168 u8,9377 u8,
...@@ -9197,11 +9406,12 @@ fn getOrPutStringValue(...@@ -9197,11 +9406,12 @@ fn getOrPutStringValue(
9197 if (map.entries[map_index].value == .none) break;9406 if (map.entries[map_index].value == .none) break;
9198 }9407 }
9199 map.entries[map_index] = .{9408 map.entries[map_index] = .{
9200 .value = value.toOptional(),9409 .value = @enumFromInt(@intFromEnum(value)),
9201 .hash = hash,9410 .hash = hash,
9202 };9411 };
9203 shard.shared.string_map.release(new_map);9412 shard.shared.string_map.release(new_map);
9204 return .none;9413 strings.appendAssumeCapacity(.{0});
9414 return value;
9205}9415}
92069416
9207pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {9417pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {
src/Value.zig+10-7
...@@ -65,8 +65,9 @@ pub fn toIpString(val: Value, ty: Type, pt: Zcu.PerThread) !InternPool.NullTermi...@@ -65,8 +65,9 @@ pub fn toIpString(val: Value, ty: Type, pt: Zcu.PerThread) !InternPool.NullTermi
65 .elems => return arrayToIpString(val, ty.arrayLen(mod), pt),65 .elems => return arrayToIpString(val, ty.arrayLen(mod), pt),
66 .repeated_elem => |elem| {66 .repeated_elem => |elem| {
67 const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(pt));67 const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(pt));
68 const len: usize = @intCast(ty.arrayLen(mod));68 const len: u32 = @intCast(ty.arrayLen(mod));
69 try ip.string_bytes.appendNTimes(mod.gpa, byte, len);69 const strings = ip.getLocal(pt.tid).getMutableStrings(mod.gpa);
70 try strings.appendNTimes(.{byte}, len);
70 return ip.getOrPutTrailingString(mod.gpa, pt.tid, len, .no_embedded_nulls);71 return ip.getOrPutTrailingString(mod.gpa, pt.tid, len, .no_embedded_nulls);
71 },72 },
72 }73 }
...@@ -107,16 +108,18 @@ fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.Null...@@ -107,16 +108,18 @@ fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.Null
107 const mod = pt.zcu;108 const mod = pt.zcu;
108 const gpa = mod.gpa;109 const gpa = mod.gpa;
109 const ip = &mod.intern_pool;110 const ip = &mod.intern_pool;
110 const len: usize = @intCast(len_u64);111 const len: u32 = @intCast(len_u64);
111 try ip.string_bytes.ensureUnusedCapacity(gpa, len);112 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa);
113 const strings_len = strings.lenPtr();
114 try strings.ensureUnusedCapacity(len);
112 for (0..len) |i| {115 for (0..len) |i| {
113 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's116 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
114 // assert just to be sure.117 // assert just to be sure.
115 const prev = ip.string_bytes.items.len;118 const prev_len = strings_len.*;
116 const elem_val = try val.elemValue(pt, i);119 const elem_val = try val.elemValue(pt, i);
117 assert(ip.string_bytes.items.len == prev);120 assert(strings_len.* == prev_len);
118 const byte: u8 = @intCast(elem_val.toUnsignedInt(pt));121 const byte: u8 = @intCast(elem_val.toUnsignedInt(pt));
119 ip.string_bytes.appendAssumeCapacity(byte);122 strings.appendAssumeCapacity(.{byte});
120 }123 }
121 return ip.getOrPutTrailingString(gpa, pt.tid, len, .no_embedded_nulls);124 return ip.getOrPutTrailingString(gpa, pt.tid, len, .no_embedded_nulls);
122}125}
src/Zcu.zig+25-16
...@@ -693,38 +693,39 @@ pub const Namespace = struct {...@@ -693,38 +693,39 @@ pub const Namespace = struct {
693 ) !InternPool.NullTerminatedString {693 ) !InternPool.NullTerminatedString {
694 const zcu = pt.zcu;694 const zcu = pt.zcu;
695 const ip = &zcu.intern_pool;695 const ip = &zcu.intern_pool;
696 const count = count: {696
697 const gpa = zcu.gpa;
698 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa);
699 // Protects reads of interned strings from being reallocated during the call to
700 // renderFullyQualifiedName.
701 const slice = try strings.addManyAsSlice(count: {
697 var count: usize = name.length(ip) + 1;702 var count: usize = name.length(ip) + 1;
698 var cur_ns = &ns;703 var cur_ns = &ns;
699 while (true) {704 while (true) {
700 const decl = zcu.declPtr(cur_ns.decl_index);705 const decl = zcu.declPtr(cur_ns.decl_index);
701 count += decl.name.length(ip) + 1;
702 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse {706 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse {
703 count += ns.fileScope(zcu).sub_file_path.len;707 count += ns.fileScope(zcu).fullyQualifiedNameLen();
704 break :count count;708 break :count count;
705 });709 });
710 count += decl.name.length(ip) + 1;
706 }711 }
707 };712 });
708713 var fbs = std.io.fixedBufferStream(slice[0]);
709 const gpa = zcu.gpa;714 ns.renderFullyQualifiedName(zcu, name, fbs.writer()) catch unreachable;
710 const start = ip.string_bytes.items.len;715 assert(fbs.pos == slice[0].len);
711 // Protects reads of interned strings from being reallocated during the call to
712 // renderFullyQualifiedName.
713 try ip.string_bytes.ensureUnusedCapacity(gpa, count);
714 ns.renderFullyQualifiedName(zcu, name, ip.string_bytes.writer(gpa)) catch unreachable;
715716
716 // Sanitize the name for nvptx which is more restrictive.717 // Sanitize the name for nvptx which is more restrictive.
717 // TODO This should be handled by the backend, not the frontend. Have a718 // TODO This should be handled by the backend, not the frontend. Have a
718 // look at how the C backend does it for inspiration.719 // look at how the C backend does it for inspiration.
719 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;720 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
720 if (cpu_arch.isNvptx()) {721 if (cpu_arch.isNvptx()) {
721 for (ip.string_bytes.items[start..]) |*byte| switch (byte.*) {722 for (slice[0]) |*byte| switch (byte.*) {
722 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_',723 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_',
723 else => {},724 else => {},
724 };725 };
725 }726 }
726727
727 return ip.getOrPutTrailingString(gpa, pt.tid, ip.string_bytes.items.len - start, .no_embedded_nulls);728 return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(slice[0].len), .no_embedded_nulls);
728 }729 }
729730
730 pub fn getType(ns: Namespace, zcu: *Zcu) Type {731 pub fn getType(ns: Namespace, zcu: *Zcu) Type {
...@@ -859,6 +860,11 @@ pub const File = struct {...@@ -859,6 +860,11 @@ pub const File = struct {
859 return &file.tree;860 return &file.tree;
860 }861 }
861862
863 pub fn fullyQualifiedNameLen(file: File) usize {
864 const ext = std.fs.path.extension(file.sub_file_path);
865 return file.sub_file_path.len - ext.len;
866 }
867
862 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {868 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
863 // Convert all the slashes into dots and truncate the extension.869 // Convert all the slashes into dots and truncate the extension.
864 const ext = std.fs.path.extension(file.sub_file_path);870 const ext = std.fs.path.extension(file.sub_file_path);
...@@ -879,9 +885,12 @@ pub const File = struct {...@@ -879,9 +885,12 @@ pub const File = struct {
879 pub fn fullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {885 pub fn fullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
880 const gpa = pt.zcu.gpa;886 const gpa = pt.zcu.gpa;
881 const ip = &pt.zcu.intern_pool;887 const ip = &pt.zcu.intern_pool;
882 const start = ip.string_bytes.items.len;888 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa);
883 try file.renderFullyQualifiedName(ip.string_bytes.writer(gpa));889 const slice = try strings.addManyAsSlice(file.fullyQualifiedNameLen());
884 return ip.getOrPutTrailingString(gpa, pt.tid, ip.string_bytes.items.len - start, .no_embedded_nulls);890 var fbs = std.io.fixedBufferStream(slice[0]);
891 file.renderFullyQualifiedName(fbs.writer()) catch unreachable;
892 assert(fbs.pos == slice[0].len);
893 return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(slice[0].len), .no_embedded_nulls);
885 }894 }
886895
887 pub fn fullPath(file: File, ally: Allocator) ![]u8 {896 pub fn fullPath(file: File, ally: Allocator) ![]u8 {
src/Zcu/PerThread.zig+6-5
...@@ -1377,10 +1377,11 @@ fn newEmbedFile(...@@ -1377,10 +1377,11 @@ fn newEmbedFile(
1377 };1377 };
1378 const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow;1378 const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow;
13791379
1380 const bytes = try ip.string_bytes.addManyAsSlice(gpa, try std.math.add(usize, size, 1));1380 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa);
1381 const actual_read = try file.readAll(bytes[0..size]);1381 const bytes = try strings.addManyAsSlice(try std.math.add(usize, size, 1));
1382 const actual_read = try file.readAll(bytes[0][0..size]);
1382 if (actual_read != size) return error.UnexpectedEndOfFile;1383 if (actual_read != size) return error.UnexpectedEndOfFile;
1383 bytes[size] = 0;1384 bytes[0][size] = 0;
13841385
1385 const comp = mod.comp;1386 const comp = mod.comp;
1386 switch (comp.cache_use) {1387 switch (comp.cache_use) {
...@@ -1389,7 +1390,7 @@ fn newEmbedFile(...@@ -1389,7 +1390,7 @@ fn newEmbedFile(
1389 errdefer gpa.free(copied_resolved_path);1390 errdefer gpa.free(copied_resolved_path);
1390 whole.cache_manifest_mutex.lock();1391 whole.cache_manifest_mutex.lock();
1391 defer whole.cache_manifest_mutex.unlock();1392 defer whole.cache_manifest_mutex.unlock();
1392 try man.addFilePostContents(copied_resolved_path, bytes[0..size], stat);1393 try man.addFilePostContents(copied_resolved_path, bytes[0][0..size], stat);
1393 },1394 },
1394 .incremental => {},1395 .incremental => {},
1395 }1396 }
...@@ -1401,7 +1402,7 @@ fn newEmbedFile(...@@ -1401,7 +1402,7 @@ fn newEmbedFile(
1401 } });1402 } });
1402 const array_val = try pt.intern(.{ .aggregate = .{1403 const array_val = try pt.intern(.{ .aggregate = .{
1403 .ty = array_ty,1404 .ty = array_ty,
1404 .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, pt.tid, bytes.len, .maybe_embedded_nulls) },1405 .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, pt.tid, @intCast(bytes[0].len), .maybe_embedded_nulls) },
1405 } });1406 } });
14061407
1407 const ptr_ty = (try pt.ptrType(.{1408 const ptr_ty = (try pt.ptrType(.{