| ... | @@ -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.File | 3 | //! * Module.Namespace has a pointer to Module.File |
| 4 | | 4 | |
| 5 | local: []Local = &.{}, | 5 | locals: []Local = &.{}, |
| 6 | shard_shift: std.math.Log2Int(usize) = 0, | | |
| 7 | shards: []Shard = &.{}, | 6 | shards: []Shard = &.{}, |
| | 7 | tid_width: std.math.Log2Int(u32) = 0, |
| | 8 | tid_shift_31: std.math.Log2Int(u32) = 31, |
| | 9 | tid_shift_32: std.math.Log2Int(u32) = 31, |
| 8 | | 10 | |
| 9 | items: std.MultiArrayList(Item) = .{}, | 11 | items: std.MultiArrayList(Item) = .{}, |
| 10 | extra: std.ArrayListUnmanaged(u32) = .{}, | 12 | extra: 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 not | 15 | /// Use the helper methods instead of accessing this directly in order to not |
| 14 | /// violate the above mechanism. | 16 | /// violate the above mechanism. |
| 15 | limbs: std.ArrayListUnmanaged(u64) = .{}, | 17 | limbs: 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. | | |
| 21 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, | | |
| 22 | | 18 | |
| 23 | /// Rather than allocating Decl objects with an Allocator, we instead allocate | 19 | /// 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 | }; |
| 346 | | 342 | |
| 347 | const Local = struct { | 343 | const 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), |
| 349 | | 349 | |
| 350 | /// header: List.Header, | 350 | const Shared = struct { |
| 351 | /// data: [capacity]u32, | 351 | strings: Strings, |
| 352 | /// tag: [header.capacity]Tag, | 352 | }; |
| 353 | items: List, | | |
| 354 | | 353 | |
| 355 | /// header: List.Header, | 354 | const Strings = List(struct { u8 }); |
| 356 | /// extra: [header.capacity]u32, | | |
| 357 | extra: List, | | |
| 358 | | 355 | |
| 359 | /// header: List.Header, | 356 | const Mutate = struct { |
| 360 | /// bytes: [header.capacity]u8, | 357 | len: u32, |
| 361 | strings: List, | | |
| 362 | | 358 | |
| 363 | arena: std.heap.ArenaAllocator.State, | 359 | const empty: Mutate = .{ |
| | 360 | .len = 0, |
| | 361 | }; |
| | 362 | }; |
| 364 | | 363 | |
| 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 | } |
| 367 | | 405 | |
| 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) }; | | |
| 375 | | 411 | |
| 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 | } |
| 382 | | 429 | |
| 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) }; |
| 386 | | 525 | |
| 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 | }; |
| | 569 | pub fn getLocal(ip: *InternPool, tid: Zcu.PerThread.Id) *Local { |
| | 570 | return &ip.locals[@intFromEnum(tid)]; |
| | 571 | } |
| | 572 | pub fn getLocalShared(ip: *const InternPool, tid: Zcu.PerThread.Id) *const Local.Shared { |
| | 573 | return &ip.locals[@intFromEnum(tid)].shared; |
| | 574 | } |
| 394 | | 575 | |
| 395 | const Shard = struct { | 576 | const 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 | } |
| 453 | | 634 | |
| 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 | }; |
| | 649 | fn getShard(ip: *InternPool, tid: Zcu.PerThread.Id) *Shard { |
| | 650 | return &ip.shards[@intFromEnum(tid)]; |
| | 651 | } |
| | 652 | |
| | 653 | fn getTidMask(ip: *const InternPool) u32 { |
| | 654 | assert(std.math.isPowerOfTwo(ip.shards.len)); |
| | 655 | return @intCast(ip.shards.len - 1); |
| | 656 | } |
| | 657 | fn getIndexMask(ip: *const InternPool, comptime BackingInt: type) u32 { |
| | 658 | return @as(u32, std.math.maxInt(BackingInt)) >> ip.tid_width; |
| | 659 | } |
| 468 | | 660 | |
| 469 | const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false); | 661 | const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false); |
| 470 | | 662 | |
| ... | @@ -560,18 +752,18 @@ pub const OptionalNamespaceIndex = enum(u32) { | ... | @@ -560,18 +752,18 @@ pub const OptionalNamespaceIndex = enum(u32) { |
| 560 | } | 752 | } |
| 561 | }; | 753 | }; |
| 562 | | 754 | |
| 563 | /// An index into `string_bytes`. | 755 | /// An index into `strings`. |
| 564 | pub const String = enum(u32) { | 756 | pub const String = enum(u32) { |
| 565 | /// An empty string. | 757 | /// An empty string. |
| 566 | empty = 0, | 758 | empty = 0, |
| 567 | _, | 759 | _, |
| 568 | | 760 | |
| 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 | } |
| 572 | | 764 | |
| 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 | } |
| 576 | | 768 | |
| 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 | }; |
| 583 | | 798 | |
| 584 | /// An index into `string_bytes` which might be `none`. | 799 | /// An index into `strings` which might be `none`. |
| 585 | pub const OptionalString = enum(u32) { | 800 | pub 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 | }; |
| 599 | | 814 | |
| 600 | /// An index into `string_bytes`. | 815 | /// An index into `strings`. |
| 601 | pub const NullTerminatedString = enum(u32) { | 816 | pub 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 | } |
| 625 | | 840 | |
| 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 | } |
| 634 | | 845 | |
| ... | @@ -637,7 +848,7 @@ pub const NullTerminatedString = enum(u32) { | ... | @@ -637,7 +848,7 @@ pub const NullTerminatedString = enum(u32) { |
| 637 | } | 848 | } |
| 638 | | 849 | |
| 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 and | 852 | return overlong_slice.len > slice.len and |
| 642 | std.mem.eql(u8, overlong_slice[0..slice.len], slice) and | 853 | 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 | } |
| 690 | | 901 | |
| 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 | }; |
| 695 | | 906 | |
| 696 | /// An index into `string_bytes` which might be `none`. | 907 | /// An index into `strings` which might be `none`. |
| 697 | pub const OptionalNullTerminatedString = enum(u32) { | 908 | pub 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) { |
| 4077 | pub const Bytes = struct { | 4288 | pub const Bytes = struct { |
| 4078 | /// The type of the aggregate | 4289 | /// 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 | }; |
| 4083 | | 4294 | |
| ... | @@ -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); |
| 4649 | | 4860 | |
| 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 | }); |
| 4657 | | 4871 | |
| 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); | | |
| 4709 | | 4924 | |
| 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); |
| 4733 | | 4948 | |
| 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); |
| 4737 | | 4952 | |
| 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 | } |
| 6195 | | 6411 | |
| 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 | } |
| 9058 | | 9274 | |
| 9059 | pub fn getOrPutStringFmt( | 9275 | pub 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 invalidated | 9283 | // 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 | } |
| 9074 | | 9291 | |
| ... | @@ -9083,47 +9300,33 @@ pub fn getOrPutStringOpt( | ... | @@ -9083,47 +9300,33 @@ pub fn getOrPutStringOpt( |
| 9083 | return string.toOptional(); | 9300 | return string.toOptional(); |
| 9084 | } | 9301 | } |
| 9085 | | 9302 | |
| 9086 | /// Uses the last len bytes of ip.string_bytes as the key. | 9303 | /// Uses the last len bytes of strings as the key. |
| 9087 | pub fn getOrPutTrailingString( | 9304 | pub 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 | } | | |
| 9119 | | 9329 | |
| 9120 | fn 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 | } |
| 9206 | | 9416 | |
| 9207 | pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString { | 9417 | pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString { |