diff options
| author | Matthew Borkowski <matthew.h.borkowski@gmail.com> | 2021-11-01 01:42:02 -0400 |
|---|---|---|
| committer | Matthew Borkowski <matthew.h.borkowski@gmail.com> | 2021-11-01 05:41:37 -0400 |
| commit | e712f87a662e9f2711698bc5a74644a5b3b890f3 (patch) | |
| tree | ec2408c3c71dc3c87dda625e82a58227634c5257 /src | |
| parent | f0260555d6164642569af9e09f57458a938d5de1 (diff) | |
| download | zig-e712f87a662e9f2711698bc5a74644a5b3b890f3.tar.gz zig-e712f87a662e9f2711698bc5a74644a5b3b890f3.zip | |
astgen.zig: replace WipDecls with WipMembers, use one allocation to collect container decls, fields, and bits, instead of up to four
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 621 |
1 files changed, 273 insertions, 348 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index c199479e75..c2f7b92505 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2995,38 +2995,112 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.I return rvalue(gz, rl, result, node); } -const WipDecls = struct { - decl_index: usize = 0, - cur_bit_bag: u32 = 0, - bit_bag: ArrayListUnmanaged(u32) = .{}, - payload: ArrayListUnmanaged(u32) = .{}, +const WipMembers = struct { + payload: []u32, + decls_start: u32, + field_bits_start: u32, + fields_start: u32, + decls_end: u32, + fields_end: u32, + decl_index: u32 = 0, + field_index: u32 = 0, + + const Self = @This(); + /// struct, union, enum, and opaque decls all use same 4 bits per decl + const bits_per_decl = 4; + const decls_per_u32 = 32 / bits_per_decl; + /// struct, union, enum, and opaque decls all have maximum size of 10 u32 slots + /// (4 for src_hash + line + name + value + align + link_section + address_space) + const max_decl_size = 10; + + pub fn init(gpa: *Allocator, decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { + const decls_start = (decl_count + decls_per_u32 - 1) / decls_per_u32; + const field_bits_start = decls_start + decl_count * max_decl_size; + const fields_start = if (bits_per_field > 0) blk: { + const fields_per_u32 = 32 / bits_per_field; + break :blk field_bits_start + (field_count + fields_per_u32 - 1) / fields_per_u32; + } else field_bits_start; + const capacity = fields_start + field_count * max_field_size; + return Self{ + .payload = try gpa.alloc(u32, capacity), + .decls_start = decls_start, + .field_bits_start = field_bits_start, + .fields_start = fields_start, + .decls_end = decls_start, + .fields_end = fields_start, + }; + } - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - - fn next( - wip_decls: *WipDecls, - gpa: *Allocator, - is_pub: bool, - is_export: bool, - has_align: bool, - has_section_or_addrspace: bool, - ) Allocator.Error!void { - if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) { - try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag); - wip_decls.cur_bit_bag = 0; - } - wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> bits_per_field) | + pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { + const index = self.decl_index / decls_per_u32; + assert(index < self.decls_start); + const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload[index]; + self.payload[index] = (bit_bag >> bits_per_decl) | (@as(u32, @boolToInt(is_pub)) << 28) | (@as(u32, @boolToInt(is_export)) << 29) | (@as(u32, @boolToInt(has_align)) << 30) | (@as(u32, @boolToInt(has_section_or_addrspace)) << 31); - wip_decls.decl_index += 1; + self.decl_index += 1; + } + + pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { + const fields_per_u32 = 32 / bits_per_field; + const index = self.field_bits_start + self.field_index / fields_per_u32; + assert(index < self.fields_start); + var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload[index]; + bit_bag >>= bits_per_field; + comptime var i = 0; + inline while (i < bits_per_field) : (i += 1) { + bit_bag |= @as(u32, @boolToInt(bits[i])) << (32 - bits_per_field + i); + } + self.payload[index] = bit_bag; + self.field_index += 1; + } + + pub fn appendToDecl(self: *Self, data: u32) void { + assert(self.decls_end < self.field_bits_start); + self.payload[self.decls_end] = data; + self.decls_end += 1; + } + + pub fn appendToDeclSlice(self: *Self, data: []const u32) void { + assert(self.decls_end + data.len <= self.field_bits_start); + mem.copy(u32, self.payload[self.decls_end..], data); + self.decls_end += @intCast(u32, data.len); + } + + pub fn appendToField(self: *Self, data: u32) void { + assert(self.fields_end < self.payload.len); + self.payload[self.fields_end] = data; + self.fields_end += 1; } - fn deinit(wip_decls: *WipDecls, gpa: *Allocator) void { - wip_decls.bit_bag.deinit(gpa); - wip_decls.payload.deinit(gpa); + pub fn finishBits(self: *Self, comptime bits_per_field: u32) void { + const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32); + if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) { + const index = self.decl_index / decls_per_u32; + self.payload[index] >>= @intCast(u5, empty_decl_slots * bits_per_decl); + } + if (bits_per_field > 0) { + const fields_per_u32 = 32 / bits_per_field; + const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32); + if (self.field_index > 0 and empty_field_slots < fields_per_u32) { + const index = self.field_bits_start + self.field_index / fields_per_u32; + self.payload[index] >>= @intCast(u5, empty_field_slots * bits_per_field); + } + } + } + + pub fn declsSlice(self: *Self) []u32 { + return self.payload[0..self.decls_end]; + } + + pub fn fieldsSlice(self: *Self) []u32 { + return self.payload[self.field_bits_start..self.fields_end]; + } + + pub fn deinit(self: *Self, gpa: *Allocator) void { + gpa.free(self.payload); } }; @@ -3034,7 +3108,7 @@ fn fnDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_decls: *WipDecls, + wip_members: *WipMembers, decl_node: Ast.Node.Index, body_node: Ast.Node.Index, fn_proto: Ast.full.FnProto, @@ -3086,7 +3160,7 @@ fn fnDecl( break :blk token_tags[maybe_inline_token] == .keyword_inline; }; const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0; - try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace); + wip_members.nextDecl(is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace); var params_scope = &fn_gz.base; const is_var_args = is_var_args: { @@ -3287,25 +3361,23 @@ fn fnDecl( _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst); try decl_gz.setBlockBody(block_inst); - try wip_decls.payload.ensureUnusedCapacity(gpa, 10); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); const casted = @bitCast([4]u32, contents_hash); - wip_decls.payload.appendSliceAssumeCapacity(&casted); + wip_members.appendToDeclSlice(&casted); } { const line_delta = decl_gz.decl_line - gz.decl_line; - wip_decls.payload.appendAssumeCapacity(line_delta); + wip_members.appendToDecl(line_delta); } - wip_decls.payload.appendAssumeCapacity(fn_name_str_index); - wip_decls.payload.appendAssumeCapacity(block_inst); + wip_members.appendToDecl(fn_name_str_index); + wip_members.appendToDecl(block_inst); if (align_inst != .none) { - wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); + wip_members.appendToDecl(@enumToInt(align_inst)); } - if (has_section_or_addrspace) { - wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst)); - wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst)); + wip_members.appendToDecl(@enumToInt(section_inst)); + wip_members.appendToDecl(@enumToInt(addrspace_inst)); } } @@ -3313,7 +3385,7 @@ fn globalVarDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_decls: *WipDecls, + wip_members: *WipMembers, node: Ast.Node.Index, var_decl: Ast.full.VarDecl, ) InnerError!void { @@ -3359,7 +3431,7 @@ fn globalVarDecl( break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node); }; const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none; - try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, has_section_or_addrspace); + wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace); const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { if (!is_mutable) { @@ -3437,24 +3509,23 @@ fn globalVarDecl( _ = try block_scope.addBreak(.break_inline, block_inst, var_inst); try block_scope.setBlockBody(block_inst); - try wip_decls.payload.ensureUnusedCapacity(gpa, 10); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); const casted = @bitCast([4]u32, contents_hash); - wip_decls.payload.appendSliceAssumeCapacity(&casted); + wip_members.appendToDeclSlice(&casted); } { const line_delta = block_scope.decl_line - gz.decl_line; - wip_decls.payload.appendAssumeCapacity(line_delta); + wip_members.appendToDecl(line_delta); } - wip_decls.payload.appendAssumeCapacity(name_str_index); - wip_decls.payload.appendAssumeCapacity(block_inst); + wip_members.appendToDecl(name_str_index); + wip_members.appendToDecl(block_inst); if (align_inst != .none) { - wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); + wip_members.appendToDecl(@enumToInt(align_inst)); } if (has_section_or_addrspace) { - wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst)); - wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst)); + wip_members.appendToDecl(@enumToInt(section_inst)); + wip_members.appendToDecl(@enumToInt(addrspace_inst)); } } @@ -3462,7 +3533,7 @@ fn comptimeDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_decls: *WipDecls, + wip_members: *WipMembers, node: Ast.Node.Index, ) InnerError!void { const gpa = astgen.gpa; @@ -3473,7 +3544,7 @@ fn comptimeDecl( // Up top so the ZIR instruction index marks the start range of this // top-level declaration. const block_inst = try gz.addBlock(.block_inline, node); - try wip_decls.next(gpa, false, false, false, false); + wip_members.nextDecl(false, false, false, false); var decl_block: GenZir = .{ .force_comptime = true, @@ -3491,25 +3562,24 @@ fn comptimeDecl( } try decl_block.setBlockBody(block_inst); - try wip_decls.payload.ensureUnusedCapacity(gpa, 7); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); const casted = @bitCast([4]u32, contents_hash); - wip_decls.payload.appendSliceAssumeCapacity(&casted); + wip_members.appendToDeclSlice(&casted); } { const line_delta = decl_block.decl_line - gz.decl_line; - wip_decls.payload.appendAssumeCapacity(line_delta); + wip_members.appendToDecl(line_delta); } - wip_decls.payload.appendAssumeCapacity(0); - wip_decls.payload.appendAssumeCapacity(block_inst); + wip_members.appendToDecl(0); + wip_members.appendToDecl(block_inst); } fn usingnamespaceDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_decls: *WipDecls, + wip_members: *WipMembers, node: Ast.Node.Index, ) InnerError!void { const gpa = astgen.gpa; @@ -3526,7 +3596,7 @@ fn usingnamespaceDecl( // Up top so the ZIR instruction index marks the start range of this // top-level declaration. const block_inst = try gz.addBlock(.block_inline, node); - try wip_decls.next(gpa, is_pub, true, false, false); + wip_members.nextDecl(is_pub, true, false, false); var decl_block: GenZir = .{ .force_comptime = true, @@ -3542,25 +3612,24 @@ fn usingnamespaceDecl( _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst); try decl_block.setBlockBody(block_inst); - try wip_decls.payload.ensureUnusedCapacity(gpa, 7); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); const casted = @bitCast([4]u32, contents_hash); - wip_decls.payload.appendSliceAssumeCapacity(&casted); + wip_members.appendToDeclSlice(&casted); } { const line_delta = decl_block.decl_line - gz.decl_line; - wip_decls.payload.appendAssumeCapacity(line_delta); + wip_members.appendToDecl(line_delta); } - wip_decls.payload.appendAssumeCapacity(0); - wip_decls.payload.appendAssumeCapacity(block_inst); + wip_members.appendToDecl(0); + wip_members.appendToDecl(block_inst); } fn testDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_decls: *WipDecls, + wip_members: *WipMembers, node: Ast.Node.Index, ) InnerError!void { const gpa = astgen.gpa; @@ -3572,7 +3641,7 @@ fn testDecl( // top-level declaration. const block_inst = try gz.addBlock(.block_inline, node); - try wip_decls.next(gpa, false, false, false, false); + wip_members.nextDecl(false, false, false, false); var decl_block: GenZir = .{ .force_comptime = true, @@ -3643,18 +3712,17 @@ fn testDecl( _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); try decl_block.setBlockBody(block_inst); - try wip_decls.payload.ensureUnusedCapacity(gpa, 7); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); const casted = @bitCast([4]u32, contents_hash); - wip_decls.payload.appendSliceAssumeCapacity(&casted); + wip_members.appendToDeclSlice(&casted); } { const line_delta = decl_block.decl_line - gz.decl_line; - wip_decls.payload.appendAssumeCapacity(line_delta); + wip_members.appendToDecl(line_delta); } - wip_decls.payload.appendAssumeCapacity(test_name); - wip_decls.payload.appendAssumeCapacity(block_inst); + wip_members.appendToDecl(test_name); + wip_members.appendToDecl(block_inst); } fn structDeclInner( @@ -3705,25 +3773,15 @@ fn structDeclInner( }; defer block_scope.instructions.deinit(gpa); - try astgen.scanDecls(&namespace, container_decl.ast.members); - - var wip_decls: WipDecls = .{}; - defer wip_decls.deinit(gpa); - - // We don't know which members are fields until we iterate, so cannot do - // an accurate ensureTotalCapacity yet. - var fields_data = ArrayListUnmanaged(u32){}; - defer fields_data.deinit(gpa); + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); + const field_count = @intCast(u32, container_decl.ast.members.len - decl_count); const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - // We only need this if there are greater than fields_per_u32 fields. - var bit_bag = ArrayListUnmanaged(u32){}; - defer bit_bag.deinit(gpa); + const max_field_size = 4; + var wip_members = try WipMembers.init(gpa, decl_count, field_count, bits_per_field, max_field_size); + defer wip_members.deinit(gpa); var known_has_bits = false; - var cur_bit_bag: u32 = 0; - var field_index: usize = 0; for (container_decl.ast.members) |member_node| { const member = switch (node_tags[member_node]) { .container_field_init => tree.containerFieldInit(member_node), @@ -3736,14 +3794,14 @@ fn structDeclInner( switch (node_tags[fn_proto]) { .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3751,14 +3809,14 @@ fn structDeclInner( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3769,14 +3827,14 @@ fn structDeclInner( }, .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3784,14 +3842,14 @@ fn structDeclInner( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3799,28 +3857,28 @@ fn structDeclInner( }, .global_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .local_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .simple_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .aligned_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3828,21 +3886,21 @@ fn structDeclInner( }, .@"comptime" => { - astgen.comptimeDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.comptimeDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .@"usingnamespace" => { - astgen.usingnamespaceDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.usingnamespaceDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .test_decl => { - astgen.testDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.testDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -3850,14 +3908,9 @@ fn structDeclInner( }, else => unreachable, }; - if (field_index % fields_per_u32 == 0 and field_index != 0) { - try bit_bag.append(gpa, cur_bit_bag); - cur_bit_bag = 0; - } - try fields_data.ensureUnusedCapacity(gpa, 4); const field_name = try astgen.identAsString(member.ast.name_token); - fields_data.appendAssumeCapacity(field_name); + wip_members.appendToField(field_name); if (member.ast.type_expr == 0) { return astgen.failTok(member.ast.name_token, "struct field missing type", .{}); @@ -3867,7 +3920,7 @@ fn structDeclInner( .none else try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); - fields_data.appendAssumeCapacity(@enumToInt(field_type)); + wip_members.appendToField(@enumToInt(field_type)); known_has_bits = known_has_bits or nodeImpliesRuntimeBits(tree, member.ast.type_expr); @@ -3875,39 +3928,22 @@ fn structDeclInner( const have_value = member.ast.value_expr != 0; const is_comptime = member.comptime_token != null; const unused = false; - cur_bit_bag = (cur_bit_bag >> bits_per_field) | - (@as(u32, @boolToInt(have_align)) << 28) | - (@as(u32, @boolToInt(have_value)) << 29) | - (@as(u32, @boolToInt(is_comptime)) << 30) | - (@as(u32, @boolToInt(unused)) << 31); + wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, unused }); if (have_align) { const align_inst = try expr(&block_scope, &namespace.base, align_rl, member.ast.align_expr); - fields_data.appendAssumeCapacity(@enumToInt(align_inst)); + wip_members.appendToField(@enumToInt(align_inst)); } if (have_value) { const rl: ResultLoc = if (field_type == .none) .none else .{ .ty = field_type }; const default_inst = try expr(&block_scope, &namespace.base, rl, member.ast.value_expr); - fields_data.appendAssumeCapacity(@enumToInt(default_inst)); + wip_members.appendToField(@enumToInt(default_inst)); } else if (member.comptime_token) |comptime_token| { return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); } - - field_index += 1; - } - { - const empty_slot_count = fields_per_u32 - (field_index % fields_per_u32); - if (empty_slot_count < fields_per_u32) { - cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_field); - } - } - { - const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32); - if (empty_slot_count < WipDecls.fields_per_u32) { - wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); - } } + assert(wip_members.decl_index == decl_count and wip_members.field_index == field_count); if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); @@ -3917,36 +3953,18 @@ fn structDeclInner( .src_node = node, .layout = layout, .body_len = @intCast(u32, block_scope.instructions.items.len), - .fields_len = @intCast(u32, field_index), - .decls_len = @intCast(u32, wip_decls.decl_index), + .fields_len = field_count, + .decls_len = decl_count, .known_has_bits = known_has_bits, }); - // zig fmt: off - try astgen.extra.ensureUnusedCapacity(gpa, - bit_bag.items.len + - @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + - @boolToInt(field_index != 0) + - fields_data.items.len - ); - // zig fmt: on - - astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. - if (wip_decls.decl_index != 0) { - astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); - } - astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items); - + wip_members.finishBits(bits_per_field); + const decls_slice = wip_members.declsSlice(); + const fields_slice = wip_members.fieldsSlice(); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + block_scope.instructions.items.len + fields_slice.len); + astgen.extra.appendSliceAssumeCapacity(decls_slice); astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items); - - astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty. - if (field_index != 0) { - astgen.extra.appendAssumeCapacity(cur_bit_bag); - } - astgen.extra.appendSliceAssumeCapacity(fields_data.items); + astgen.extra.appendSliceAssumeCapacity(fields_slice); return indexToRef(decl_inst); } @@ -3989,29 +4007,19 @@ fn unionDeclInner( }; defer block_scope.instructions.deinit(gpa); - try astgen.scanDecls(&namespace, members); + const decl_count = try astgen.scanDecls(&namespace, members); + const field_count = @intCast(u32, members.len - decl_count); const arg_inst: Zir.Inst.Ref = if (arg_node != 0) try typeExpr(&block_scope, &namespace.base, arg_node) else .none; - var wip_decls: WipDecls = .{}; - defer wip_decls.deinit(gpa); - - // We don't know which members are fields until we iterate, so cannot do - // an accurate ensureTotalCapacity yet. - var fields_data = ArrayListUnmanaged(u32){}; - defer fields_data.deinit(gpa); - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - // We only need this if there are greater than fields_per_u32 fields. - var bit_bag = ArrayListUnmanaged(u32){}; - defer bit_bag.deinit(gpa); + const max_field_size = 4; + var wip_members = try WipMembers.init(gpa, decl_count, field_count, bits_per_field, max_field_size); + defer wip_members.deinit(gpa); - var cur_bit_bag: u32 = 0; - var field_index: usize = 0; for (members) |member_node| { const member = switch (node_tags[member_node]) { .container_field_init => tree.containerFieldInit(member_node), @@ -4024,14 +4032,14 @@ fn unionDeclInner( switch (node_tags[fn_proto]) { .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4039,14 +4047,14 @@ fn unionDeclInner( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4057,14 +4065,14 @@ fn unionDeclInner( }, .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4072,14 +4080,14 @@ fn unionDeclInner( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4087,28 +4095,28 @@ fn unionDeclInner( }, .global_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .local_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .simple_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .aligned_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4116,21 +4124,21 @@ fn unionDeclInner( }, .@"comptime" => { - astgen.comptimeDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.comptimeDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .@"usingnamespace" => { - astgen.usingnamespaceDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.usingnamespaceDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .test_decl => { - astgen.testDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.testDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4138,40 +4146,31 @@ fn unionDeclInner( }, else => unreachable, }; - if (field_index % fields_per_u32 == 0 and field_index != 0) { - try bit_bag.append(gpa, cur_bit_bag); - cur_bit_bag = 0; - } if (member.comptime_token) |comptime_token| { return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); } - try fields_data.ensureUnusedCapacity(gpa, 4); const field_name = try astgen.identAsString(member.ast.name_token); - fields_data.appendAssumeCapacity(field_name); + wip_members.appendToField(field_name); const have_type = member.ast.type_expr != 0; const have_align = member.ast.align_expr != 0; const have_value = member.ast.value_expr != 0; const unused = false; - cur_bit_bag = (cur_bit_bag >> bits_per_field) | - (@as(u32, @boolToInt(have_type)) << 28) | - (@as(u32, @boolToInt(have_align)) << 29) | - (@as(u32, @boolToInt(have_value)) << 30) | - (@as(u32, @boolToInt(unused)) << 31); + wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused }); if (have_type) { const field_type: Zir.Inst.Ref = if (node_tags[member.ast.type_expr] == .@"anytype") .none else try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); - fields_data.appendAssumeCapacity(@enumToInt(field_type)); + wip_members.appendToField(@enumToInt(field_type)); } else if (arg_inst == .none and !have_auto_enum) { return astgen.failNode(member_node, "union field missing type", .{}); } if (have_align) { const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr); - fields_data.appendAssumeCapacity(@enumToInt(align_inst)); + wip_members.appendToField(@enumToInt(align_inst)); } if (have_value) { if (arg_inst == .none) { @@ -4203,26 +4202,13 @@ fn unionDeclInner( ); } const tag_value = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr); - fields_data.appendAssumeCapacity(@enumToInt(tag_value)); + wip_members.appendToField(@enumToInt(tag_value)); } - - field_index += 1; } - if (field_index == 0) { + assert(wip_members.decl_index == decl_count and wip_members.field_index == field_count); + if (field_count == 0) { return astgen.failNode(node, "union declarations must have at least one tag", .{}); } - { - const empty_slot_count = fields_per_u32 - (field_index % fields_per_u32); - if (empty_slot_count < fields_per_u32) { - cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_field); - } - } - { - const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32); - if (empty_slot_count < WipDecls.fields_per_u32) { - wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); - } - } if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); @@ -4233,34 +4219,18 @@ fn unionDeclInner( .layout = layout, .tag_type = arg_inst, .body_len = @intCast(u32, block_scope.instructions.items.len), - .fields_len = @intCast(u32, field_index), - .decls_len = @intCast(u32, wip_decls.decl_index), + .fields_len = field_count, + .decls_len = decl_count, .auto_enum_tag = have_auto_enum, }); - // zig fmt: off - try astgen.extra.ensureUnusedCapacity(gpa, - bit_bag.items.len + - @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + - 1 + // cur_bit_bag - fields_data.items.len - ); - // zig fmt: on - - astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. - if (wip_decls.decl_index != 0) { - astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); - } - astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items); - + wip_members.finishBits(bits_per_field); + const decls_slice = wip_members.declsSlice(); + const fields_slice = wip_members.fieldsSlice(); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + block_scope.instructions.items.len + fields_slice.len); + astgen.extra.appendSliceAssumeCapacity(decls_slice); astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items); - - astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty. - astgen.extra.appendAssumeCapacity(cur_bit_bag); - astgen.extra.appendSliceAssumeCapacity(fields_data.items); + astgen.extra.appendSliceAssumeCapacity(fields_slice); return indexToRef(decl_inst); } @@ -4434,27 +4404,18 @@ fn containerDecl( }; defer block_scope.instructions.deinit(gpa); - try astgen.scanDecls(&namespace, container_decl.ast.members); + _ = try astgen.scanDecls(&namespace, container_decl.ast.members); const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0) try comptimeExpr(&block_scope, &namespace.base, .{ .ty = .type_type }, container_decl.ast.arg) else .none; - var wip_decls: WipDecls = .{}; - defer wip_decls.deinit(gpa); - - var fields_data = ArrayListUnmanaged(u32){}; - defer fields_data.deinit(gpa); - - try fields_data.ensureTotalCapacity(gpa, counts.total_fields + counts.values); - - // We only need this if there are greater than 32 fields. - var bit_bag = ArrayListUnmanaged(u32){}; - defer bit_bag.deinit(gpa); + const bits_per_field = 1; + const max_field_size = 2; + var wip_members = try WipMembers.init(gpa, @intCast(u32, counts.decls), @intCast(u32, counts.total_fields), bits_per_field, max_field_size); + defer wip_members.deinit(gpa); - var cur_bit_bag: u32 = 0; - var field_index: usize = 0; for (container_decl.ast.members) |member_node| { if (member_node == counts.nonexhaustive_node) continue; @@ -4469,14 +4430,14 @@ fn containerDecl( switch (node_tags[fn_proto]) { .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4484,14 +4445,14 @@ fn containerDecl( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4502,14 +4463,14 @@ fn containerDecl( }, .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4517,14 +4478,14 @@ fn containerDecl( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4532,28 +4493,28 @@ fn containerDecl( }, .global_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .local_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .simple_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .aligned_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4561,21 +4522,21 @@ fn containerDecl( }, .@"comptime" => { - astgen.comptimeDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.comptimeDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .@"usingnamespace" => { - astgen.usingnamespaceDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.usingnamespaceDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .test_decl => { - astgen.testDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.testDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4583,20 +4544,15 @@ fn containerDecl( }, else => unreachable, }; - if (field_index % 32 == 0 and field_index != 0) { - try bit_bag.append(gpa, cur_bit_bag); - cur_bit_bag = 0; - } assert(member.comptime_token == null); assert(member.ast.type_expr == 0); assert(member.ast.align_expr == 0); const field_name = try astgen.identAsString(member.ast.name_token); - fields_data.appendAssumeCapacity(field_name); + wip_members.appendToField(field_name); const have_value = member.ast.value_expr != 0; - cur_bit_bag = (cur_bit_bag >> 1) | - (@as(u32, @boolToInt(have_value)) << 31); + wip_members.nextField(bits_per_field, .{have_value}); if (have_value) { if (arg_inst == .none) { @@ -4614,23 +4570,10 @@ fn containerDecl( ); } const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .ty = arg_inst }, member.ast.value_expr); - fields_data.appendAssumeCapacity(@enumToInt(tag_value_inst)); - } - - field_index += 1; - } - { - const empty_slot_count = 32 - (field_index % 32); - if (empty_slot_count < 32) { - cur_bit_bag >>= @intCast(u5, empty_slot_count); - } - } - { - const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32); - if (empty_slot_count < WipDecls.fields_per_u32) { - wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); + wip_members.appendToField(@enumToInt(tag_value_inst)); } } + assert(wip_members.decl_index == counts.decls and wip_members.field_index == counts.total_fields); if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); @@ -4641,32 +4584,17 @@ fn containerDecl( .nonexhaustive = nonexhaustive, .tag_type = arg_inst, .body_len = @intCast(u32, block_scope.instructions.items.len), - .fields_len = @intCast(u32, field_index), - .decls_len = @intCast(u32, wip_decls.decl_index), + .fields_len = @intCast(u32, counts.total_fields), + .decls_len = @intCast(u32, counts.decls), }); - // zig fmt: off - try astgen.extra.ensureUnusedCapacity(gpa, - bit_bag.items.len + - @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + - 1 + // cur_bit_bag - fields_data.items.len - ); - // zig fmt: on - - astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. - if (wip_decls.decl_index != 0) { - astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); - } - astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items); - + wip_members.finishBits(bits_per_field); + const decls_slice = wip_members.declsSlice(); + const fields_slice = wip_members.fieldsSlice(); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + block_scope.instructions.items.len + fields_slice.len); + astgen.extra.appendSliceAssumeCapacity(decls_slice); astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items); - astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty. - astgen.extra.appendAssumeCapacity(cur_bit_bag); - astgen.extra.appendSliceAssumeCapacity(fields_data.items); + astgen.extra.appendSliceAssumeCapacity(fields_slice); return rvalue(gz, rl, indexToRef(decl_inst), node); }, @@ -4683,10 +4611,10 @@ fn containerDecl( }; defer namespace.deinit(gpa); - try astgen.scanDecls(&namespace, container_decl.ast.members); + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); - var wip_decls: WipDecls = .{}; - defer wip_decls.deinit(gpa); + var wip_members = try WipMembers.init(gpa, decl_count, 0, 0, 0); + defer wip_members.deinit(gpa); for (container_decl.ast.members) |member_node| { switch (node_tags[member_node]) { @@ -4698,14 +4626,14 @@ fn containerDecl( switch (node_tags[fn_proto]) { .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoSimple(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4713,14 +4641,14 @@ fn containerDecl( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProtoOne(¶ms, fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4731,14 +4659,14 @@ fn containerDecl( }, .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoSimple(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto_multi => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4746,14 +4674,14 @@ fn containerDecl( }, .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProtoOne(¶ms, member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .fn_proto => { - astgen.fnDecl(gz, &namespace.base, &wip_decls, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { + astgen.fnDecl(gz, &namespace.base, &wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4761,28 +4689,28 @@ fn containerDecl( }, .global_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .local_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .simple_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .aligned_var_decl => { - astgen.globalVarDecl(gz, &namespace.base, &wip_decls, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { + astgen.globalVarDecl(gz, &namespace.base, &wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4790,21 +4718,21 @@ fn containerDecl( }, .@"comptime" => { - astgen.comptimeDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.comptimeDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .@"usingnamespace" => { - astgen.usingnamespaceDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.usingnamespaceDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; continue; }, .test_decl => { - astgen.testDecl(gz, &namespace.base, &wip_decls, member_node) catch |err| switch (err) { + astgen.testDecl(gz, &namespace.base, &wip_members, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => {}, }; @@ -4813,31 +4741,17 @@ fn containerDecl( else => unreachable, } } - { - const empty_slot_count = WipDecls.fields_per_u32 - (wip_decls.decl_index % WipDecls.fields_per_u32); - if (empty_slot_count < WipDecls.fields_per_u32) { - wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); - } - } + assert(wip_members.decl_index == decl_count); try gz.setOpaque(decl_inst, .{ .src_node = node, - .decls_len = @intCast(u32, wip_decls.decl_index), + .decls_len = decl_count, }); - // zig fmt: off - try astgen.extra.ensureUnusedCapacity(gpa, - wip_decls.bit_bag.items.len + - @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len - ); - // zig fmt: on - - astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. - if (wip_decls.decl_index != 0) { - astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); - } - astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items); + wip_members.finishBits(0); + const decls_slice = wip_members.declsSlice(); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len); + astgen.extra.appendSliceAssumeCapacity(decls_slice); return rvalue(gz, rl, indexToRef(decl_inst), node); }, @@ -10436,12 +10350,13 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void { astgen.source_column = column; } -fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void { +fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 { const gpa = astgen.gpa; const tree = astgen.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); const token_tags = tree.tokens.items(.tag); + var decl_count: u32 = 0; for (members) |member_node| { const name_token = switch (node_tags[member_node]) { .fn_proto_simple, @@ -10452,9 +10367,13 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. .local_var_decl, .simple_var_decl, .aligned_var_decl, - => main_tokens[member_node] + 1, + => blk: { + decl_count += 1; + break :blk main_tokens[member_node] + 1; + }, .fn_decl => blk: { + decl_count += 1; const ident = main_tokens[member_node] + 1; if (token_tags[ident] != .identifier) { switch (astgen.failNode(member_node, "missing function name", .{})) { @@ -10465,6 +10384,11 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. break :blk ident; }, + .@"comptime", .@"usingnamespace", .test_decl => { + decl_count += 1; + continue; + }, + else => continue, }; @@ -10498,4 +10422,5 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. } gop.value_ptr.* = member_node; } + return decl_count; } |
