diff --git a/src/AstGen.zig b/src/AstGen.zig index f709751fde3c..caa0efa85132 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -86,6 +86,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { Zir.Inst.Ref, Zir.Inst.Index, + Zir.Inst.Declaration.Name, Zir.NullTerminatedString, => @intFromEnum(@field(extra, field.name)), @@ -95,6 +96,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { Zir.Inst.SwitchBlock.Bits, Zir.Inst.SwitchBlockErrUnion.Bits, Zir.Inst.FuncFancy.Bits, + Zir.Inst.Declaration.Flags, => @bitCast(@field(extra, field.name)), else => @compileError("bad field type"), @@ -132,8 +134,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { }; defer astgen.deinit(gpa); - // String table indexes 0, 1, 2 are reserved for special meaning. - try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 }); + // String table index 0 is reserved for `NullTerminatedString.empty`. + try astgen.string_bytes.append(gpa, 0); // We expect at least as many ZIR instructions and extra data items // as AST nodes. @@ -355,8 +357,13 @@ const ResultInfo = struct { }; }; +/// TODO: modify Sema to remove in favour of `coerced_align_ri` const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } }; const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; +/// TODO: modify Sema to remove in favour of `coerced_addrspace_ri` +const addrspace_ri: ResultInfo = .{ .rl = .{ .ty = .address_space_type } }; +const coerced_addrspace_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .address_space_type } }; +const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }; const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } }; const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } }; const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; @@ -2592,6 +2599,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .block, .block_comptime, .block_inline, + .declaration, .suspend_block, .loop, .bool_br_and, @@ -3783,7 +3791,7 @@ fn ptrType( gz.astgen.source_line = source_line; gz.astgen.source_column = source_column; - addrspace_ref = try expr(gz, scope, .{ .rl = .{ .ty = .address_space_type } }, ptr_info.ast.addrspace_node); + addrspace_ref = try expr(gz, scope, addrspace_ri, ptr_info.ast.addrspace_node); trailing_count += 1; } if (ptr_info.ast.align_node != 0) { @@ -3899,8 +3907,6 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node. const WipMembers = struct { payload: *ArrayListUnmanaged(u32), payload_top: usize, - decls_start: u32, - decls_end: u32, field_bits_start: u32, fields_start: u32, fields_end: u32, @@ -3908,43 +3914,27 @@ const WipMembers = struct { 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 11 u32 slots - /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space ) - const max_decl_size = 11; fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { const payload_top: u32 = @intCast(payload.items.len); - const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32; - const field_bits_start = decls_start + decl_count * max_decl_size; + const field_bits_start = payload_top + decl_count; const fields_start = field_bits_start + if (bits_per_field > 0) blk: { const fields_per_u32 = 32 / bits_per_field; break :blk (field_count + fields_per_u32 - 1) / fields_per_u32; } else 0; const payload_end = fields_start + field_count * max_field_size; try payload.resize(gpa, payload_end); - return Self{ + return .{ .payload = payload, .payload_top = payload_top, - .decls_start = decls_start, .field_bits_start = field_bits_start, .fields_start = fields_start, - .decls_end = decls_start, .fields_end = fields_start, }; } - fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void { - const index = self.payload_top + 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.items[index]; - self.payload.items[index] = (bit_bag >> bits_per_decl) | - (@as(u32, @intFromBool(is_pub)) << 28) | - (@as(u32, @intFromBool(is_export)) << 29) | - (@as(u32, @intFromBool(has_align)) << 30) | - (@as(u32, @intFromBool(has_section_or_addrspace)) << 31); + fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void { + self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst); self.decl_index += 1; } @@ -3962,18 +3952,6 @@ const WipMembers = struct { self.field_index += 1; } - fn appendToDecl(self: *Self, data: u32) void { - assert(self.decls_end < self.field_bits_start); - self.payload.items[self.decls_end] = data; - self.decls_end += 1; - } - - fn appendToDeclSlice(self: *Self, data: []const u32) void { - assert(self.decls_end + data.len <= self.field_bits_start); - @memcpy(self.payload.items[self.decls_end..][0..data.len], data); - self.decls_end += @intCast(data.len); - } - fn appendToField(self: *Self, data: u32) void { assert(self.fields_end < self.payload.items.len); self.payload.items[self.fields_end] = data; @@ -3981,11 +3959,6 @@ const WipMembers = struct { } 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.payload_top + self.decl_index / decls_per_u32; - self.payload.items[index] >>= @intCast(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); @@ -3997,7 +3970,7 @@ const WipMembers = struct { } fn declsSlice(self: *Self) []u32 { - return self.payload.items[self.payload_top..self.decls_end]; + return self.payload.items[self.payload_top..][0..self.decl_index]; } fn fieldsSlice(self: *Self) []u32 { @@ -4023,11 +3996,10 @@ fn fnDecl( // missing function name already happened in scanDecls() const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail; - const fn_name_str_index = try astgen.identAsString(fn_name_token); // We insert this at the beginning so that its instruction index marks the // start of the top level declaration. - const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node); + const decl_inst = try gz.makeBlockInst(.declaration, fn_proto.ast.proto_node); astgen.advanceSourceCursorToNode(decl_node); var decl_gz: GenZir = .{ @@ -4072,8 +4044,7 @@ fn fnDecl( const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken()); - // align, linksection, and addrspace is passed in the func instruction in this case. - wip_members.nextDecl(is_pub, is_export, false, false); + wip_members.nextDecl(decl_inst); var noalias_bits: u32 = 0; var params_scope = &fn_gz.base; @@ -4213,7 +4184,7 @@ fn fnDecl( var addrspace_gz = decl_gz.makeSubBlock(params_scope); defer addrspace_gz.unstack(); const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: { - const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .address_space_type } }, fn_proto.ast.addrspace_expr); + const inst = try expr(&decl_gz, params_scope, addrspace_ri, fn_proto.ast.addrspace_expr); if (addrspace_gz.instructionsSlice().len == 0) { // In this case we will send a len=0 body which can be encoded more efficiently. break :inst inst; @@ -4298,7 +4269,7 @@ fn fnDecl( .section_gz = §ion_gz, .addrspace_ref = addrspace_ref, .addrspace_gz = &addrspace_gz, - .param_block = block_inst, + .param_block = decl_inst, .body_gz = null, .lib_name = lib_name, .is_var_args = is_var_args, @@ -4349,7 +4320,7 @@ fn fnDecl( .addrspace_gz = &addrspace_gz, .lbrace_line = lbrace_line, .lbrace_column = lbrace_column, - .param_block = block_inst, + .param_block = decl_inst, .body_gz = &fn_gz, .lib_name = lib_name, .is_var_args = is_var_args, @@ -4363,20 +4334,21 @@ fn fnDecl( // We add this at the end so that its instruction index marks the end range // of the top level declaration. addFunc already unstacked fn_gz and ret_gz. - _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst); - try decl_gz.setBlockBody(block_inst); - - { - const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(decl_node)); - wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash)); - } - { - const line_delta = decl_gz.decl_line - gz.decl_line; - wip_members.appendToDecl(line_delta); - } - wip_members.appendToDecl(@intFromEnum(fn_name_str_index)); - wip_members.appendToDecl(@intFromEnum(block_inst)); - wip_members.appendToDecl(@intFromEnum(doc_comment_index)); + _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); + + try setDeclaration( + decl_inst, + std.zig.hashSrc(tree.getNodeSource(decl_node)), + .{ .named = fn_name_token }, + decl_gz.decl_line - gz.decl_line, + is_pub, + is_export, + doc_comment_index, + &decl_gz, + // align, linksection, and addrspace are passed in the func instruction in this case. + // TODO: move them from the function instruction to the declaration instruction? + null, + ); } fn globalVarDecl( @@ -4393,10 +4365,9 @@ fn globalVarDecl( const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; // We do this at the beginning so that the instruction index marks the range start // of the top level declaration. - const block_inst = try gz.makeBlockInst(.block_inline, node); + const decl_inst = try gz.makeBlockInst(.declaration, node); const name_token = var_decl.ast.mut_token + 1; - const name_str_index = try astgen.identAsString(name_token); astgen.advanceSourceCursorToNode(node); var block_scope: GenZir = .{ @@ -4420,17 +4391,7 @@ fn globalVarDecl( const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; break :blk token_tags[maybe_extern_token] == .keyword_extern; }; - const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: { - break :inst try expr(&block_scope, &block_scope.base, align_ri, var_decl.ast.align_node); - }; - const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: { - break :inst try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .address_space_type } }, var_decl.ast.addrspace_node); - }; - const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: { - break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .slice_const_u8_type } }, var_decl.ast.section_node); - }; - const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none; - wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace); + wip_members.nextDecl(decl_inst); const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { if (!is_mutable) { @@ -4513,29 +4474,44 @@ fn globalVarDecl( } else { return astgen.failNode(node, "unable to infer variable type", .{}); }; + // We do this at the end so that the instruction index marks the end // range of a top level declaration. - _ = try block_scope.addBreakWithSrcNode(.break_inline, block_inst, var_inst, node); - try block_scope.setBlockBody(block_inst); + _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node); - { - const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node)); - wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash)); + var align_gz = block_scope.makeSubBlock(scope); + if (var_decl.ast.align_node != 0) { + const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node); + _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); } - { - const line_delta = block_scope.decl_line - gz.decl_line; - wip_members.appendToDecl(line_delta); - } - wip_members.appendToDecl(@intFromEnum(name_str_index)); - wip_members.appendToDecl(@intFromEnum(block_inst)); - wip_members.appendToDecl(@intFromEnum(doc_comment_index)); // doc_comment wip - if (align_inst != .none) { - wip_members.appendToDecl(@intFromEnum(align_inst)); - } - if (has_section_or_addrspace) { - wip_members.appendToDecl(@intFromEnum(section_inst)); - wip_members.appendToDecl(@intFromEnum(addrspace_inst)); + + var linksection_gz = align_gz.makeSubBlock(scope); + if (var_decl.ast.section_node != 0) { + const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); } + + var addrspace_gz = linksection_gz.makeSubBlock(scope); + if (var_decl.ast.addrspace_node != 0) { + const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node); + _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); + } + + try setDeclaration( + decl_inst, + std.zig.hashSrc(tree.getNodeSource(node)), + .{ .named = name_token }, + block_scope.decl_line - gz.decl_line, + is_pub, + is_export, + doc_comment_index, + &block_scope, + .{ + .align_gz = &align_gz, + .linksection_gz = &linksection_gz, + .addrspace_gz = &addrspace_gz, + }, + ); } fn comptimeDecl( @@ -4551,8 +4527,8 @@ fn comptimeDecl( // Up top so the ZIR instruction index marks the start range of this // top-level declaration. - const block_inst = try gz.makeBlockInst(.block_inline, node); - wip_members.nextDecl(false, false, false, false); + const decl_inst = try gz.makeBlockInst(.declaration, node); + wip_members.nextDecl(decl_inst); astgen.advanceSourceCursorToNode(node); var decl_block: GenZir = .{ @@ -4568,21 +4544,20 @@ fn comptimeDecl( const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node); if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) { - _ = try decl_block.addBreak(.break_inline, block_inst, .void_value); + _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value); } - try decl_block.setBlockBody(block_inst); - { - const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node)); - wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash)); - } - { - const line_delta = decl_block.decl_line - gz.decl_line; - wip_members.appendToDecl(line_delta); - } - wip_members.appendToDecl(0); - wip_members.appendToDecl(@intFromEnum(block_inst)); - wip_members.appendToDecl(0); // no doc comments on comptime decls + try setDeclaration( + decl_inst, + std.zig.hashSrc(tree.getNodeSource(node)), + .@"comptime", + decl_block.decl_line - gz.decl_line, + false, + false, + .empty, + &decl_block, + null, + ); } fn usingnamespaceDecl( @@ -4604,8 +4579,8 @@ fn usingnamespaceDecl( }; // Up top so the ZIR instruction index marks the start range of this // top-level declaration. - const block_inst = try gz.makeBlockInst(.block_inline, node); - wip_members.nextDecl(is_pub, true, false, false); + const decl_inst = try gz.makeBlockInst(.declaration, node); + wip_members.nextDecl(decl_inst); astgen.advanceSourceCursorToNode(node); var decl_block: GenZir = .{ @@ -4620,20 +4595,19 @@ fn usingnamespaceDecl( defer decl_block.unstack(); const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr); - _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst); - try decl_block.setBlockBody(block_inst); - - { - const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node)); - wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash)); - } - { - const line_delta = decl_block.decl_line - gz.decl_line; - wip_members.appendToDecl(line_delta); - } - wip_members.appendToDecl(0); - wip_members.appendToDecl(@intFromEnum(block_inst)); - wip_members.appendToDecl(0); // no doc comments on usingnamespace decls + _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst); + + try setDeclaration( + decl_inst, + std.zig.hashSrc(tree.getNodeSource(node)), + .@"usingnamespace", + decl_block.decl_line - gz.decl_line, + is_pub, + false, + .empty, + &decl_block, + null, + ); } fn testDecl( @@ -4649,9 +4623,9 @@ fn testDecl( // Up top so the ZIR instruction index marks the start range of this // top-level declaration. - const block_inst = try gz.makeBlockInst(.block_inline, node); + const decl_inst = try gz.makeBlockInst(.declaration, node); - wip_members.nextDecl(false, false, false, false); + wip_members.nextDecl(decl_inst); astgen.advanceSourceCursorToNode(node); var decl_block: GenZir = .{ @@ -4669,12 +4643,10 @@ fn testDecl( const token_tags = tree.tokens.items(.tag); const test_token = main_tokens[node]; const test_name_token = test_token + 1; - const test_name_token_tag = token_tags[test_name_token]; - const is_decltest = test_name_token_tag == .identifier; - const test_name: Zir.NullTerminatedString = blk: { - if (test_name_token_tag == .string_literal) { - break :blk try astgen.testNameString(test_name_token); - } else if (test_name_token_tag == .identifier) { + const test_name: DeclarationName = switch (token_tags[test_name_token]) { + else => .unnamed_test, + .string_literal => .{ .named_test = test_name_token }, + .identifier => blk: { const ident_name_raw = tree.tokenSlice(test_name_token); if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{}); @@ -4744,10 +4716,8 @@ fn testDecl( return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name}); } - break :blk name_str_index; - } - // String table index 1 has a special meaning here of test decl with no name. - break :blk .unnamed_test_decl; + break :blk .{ .decltest = name_str_index }; + }, }; var fn_block: GenZir = .{ @@ -4795,7 +4765,7 @@ fn testDecl( .lbrace_line = lbrace_line, .lbrace_column = lbrace_column, - .param_block = block_inst, + .param_block = decl_inst, .body_gz = &fn_block, .lib_name = .empty, .is_var_args = false, @@ -4806,26 +4776,19 @@ fn testDecl( .noalias_bits = 0, }); - _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); - try decl_block.setBlockBody(block_inst); - - { - const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node)); - wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash)); - } - { - const line_delta = decl_block.decl_line - gz.decl_line; - wip_members.appendToDecl(line_delta); - } - if (is_decltest) - wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name - else - wip_members.appendToDecl(@intFromEnum(test_name)); - wip_members.appendToDecl(@intFromEnum(block_inst)); - if (is_decltest) - wip_members.appendToDecl(@intFromEnum(test_name)) // the doc comment on a decltest represents it's name - else - wip_members.appendToDecl(0); // no doc comments on test decls + _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst); + + try setDeclaration( + decl_inst, + std.zig.hashSrc(tree.getNodeSource(node)), + test_name, + decl_block.decl_line - gz.decl_line, + false, + false, + .empty, + &decl_block, + null, + ); } fn structDeclInner( @@ -13524,3 +13487,106 @@ fn lowerAstErrors(astgen: *AstGen) !void { try tree.renderError(parse_err, msg.writer(gpa)); try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items); } + +const DeclarationName = union(enum) { + named: Ast.TokenIndex, + named_test: Ast.TokenIndex, + unnamed_test, + decltest: Zir.NullTerminatedString, + @"comptime", + @"usingnamespace", +}; + +/// Sets all extra data for a `declaration` instruction. +/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`. +fn setDeclaration( + decl_inst: Zir.Inst.Index, + src_hash: std.zig.SrcHash, + name: DeclarationName, + line_offset: u32, + is_pub: bool, + is_export: bool, + doc_comment: Zir.NullTerminatedString, + value_gz: *GenZir, + /// May be `null` if all these blocks would be empty. + /// If `null`, then `value_gz` must have nothing stacked on it. + extra_gzs: ?struct { + /// Must be stacked on `value_gz`. + align_gz: *GenZir, + /// Must be stacked on `align_gz`. + linksection_gz: *GenZir, + /// Must be stacked on `linksection_gz`, and have nothing stacked on it. + addrspace_gz: *GenZir, + }, +) !void { + const astgen = value_gz.astgen; + const gpa = astgen.gpa; + + const empty_body: []Zir.Inst.Index = &.{}; + const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{ + value_gz.instructionsSliceUpto(e.align_gz), + e.align_gz.instructionsSliceUpto(e.linksection_gz), + e.linksection_gz.instructionsSliceUpto(e.addrspace_gz), + e.addrspace_gz.instructionsSlice(), + } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body }; + + const value_len = astgen.countBodyLenAfterFixups(value_body); + const align_len = astgen.countBodyLenAfterFixups(align_body); + const linksection_len = astgen.countBodyLenAfterFixups(linksection_body); + const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body); + + const true_doc_comment: Zir.NullTerminatedString = switch (name) { + .decltest => |test_name| test_name, + else => doc_comment, + }; + + const src_hash_arr: [4]u32 = @bitCast(src_hash); + + const extra: Zir.Inst.Declaration = .{ + .src_hash_0 = src_hash_arr[0], + .src_hash_1 = src_hash_arr[1], + .src_hash_2 = src_hash_arr[2], + .src_hash_3 = src_hash_arr[3], + .name = switch (name) { + .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))), + .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))), + .unnamed_test => .unnamed_test, + .decltest => .decltest, + .@"comptime" => .@"comptime", + .@"usingnamespace" => .@"usingnamespace", + }, + .line_offset = line_offset, + .flags = .{ + .value_body_len = @intCast(value_len), + .is_pub = is_pub, + .is_export = is_export, + .has_doc_comment = true_doc_comment != .empty, + .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0, + }, + }; + astgen.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node.payload_index = try astgen.addExtra(extra); + if (extra.flags.has_doc_comment) { + try astgen.extra.append(gpa, @intFromEnum(true_doc_comment)); + } + if (extra.flags.has_align_linksection_addrspace) { + try astgen.extra.appendSlice(gpa, &.{ + align_len, + linksection_len, + addrspace_len, + }); + } + try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len); + astgen.appendBodyWithFixups(value_body); + if (extra.flags.has_align_linksection_addrspace) { + astgen.appendBodyWithFixups(align_body); + astgen.appendBodyWithFixups(linksection_body); + astgen.appendBodyWithFixups(addrspace_body); + } + + if (extra_gzs) |e| { + e.addrspace_gz.unstack(); + e.linksection_gz.unstack(); + e.align_gz.unstack(); + } + value_gz.unstack(); +} diff --git a/src/Autodoc.zig b/src/Autodoc.zig index a03ed351789d..838f8174d18d 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -4078,6 +4078,9 @@ fn analyzeAllDecls( priv_decl_indexes: *std.ArrayListUnmanaged(usize), call_ctx: ?*const CallContext, ) AutodocErrors!usize { + //if (true) @compileError("TODO AUTODOC"); + if (true) @panic("TODO AUTODOC"); + const first_decl_indexes_slot = decl_indexes.items.len; const original_it = file.zir.declIterator(parent_inst); diff --git a/src/InternPool.zig b/src/InternPool.zig index 0844e0f7a586..1c5703b05564 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -6186,8 +6186,6 @@ fn finishFuncInstance( .generation = generation, .is_pub = fn_owner_decl.is_pub, .is_exported = fn_owner_decl.is_exported, - .has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace, - .has_align = fn_owner_decl.has_align, .alive = true, .kind = .anon, }); diff --git a/src/Module.zig b/src/Module.zig index 8f6f0e34d1b1..1b7d80f9cb9e 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -386,11 +386,9 @@ pub const Decl = struct { /// do not need to be loaded into memory in order to compute debug line numbers. /// This value is absolute. src_line: u32, - /// Index to ZIR `extra` array to the entry in the parent's decl structure - /// (the part that says "for every decls_len"). The first item at this index is - /// the contents hash, followed by line, name, etc. - /// For anonymous decls and also the root Decl for a File, this is `none`. - zir_decl_index: Zir.OptionalExtraIndex, + /// Index of the ZIR `declaration` instruction from which this `Decl` was created. + /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`. + zir_decl_index: Zir.Inst.OptionalIndex, /// Represents the "shallow" analysis status. For example, for decls that are functions, /// the function type is analyzed with this set to `in_progress`, however, the semantic @@ -442,10 +440,6 @@ pub const Decl = struct { is_pub: bool, /// Whether the corresponding AST decl has a `export` keyword. is_exported: bool, - /// Whether the ZIR code provides an align instruction. - has_align: bool, - /// Whether the ZIR code provides a linksection and address space instruction. - has_linksection_or_addrspace: bool, /// Flag used by garbage collection to mark and sweep. /// Decls which correspond to an AST node always have this field set to `true`. /// Anonymous Decls are initialized with this field set to `false` and then it @@ -471,23 +465,12 @@ pub const Decl = struct { const Index = InternPool.DeclIndex; const OptionalIndex = InternPool.OptionalDeclIndex; - pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, DepType); - - /// Later types take priority; e.g. if a dependent decl has both `normal` - /// and `function_body` dependencies on another decl, it will be marked as - /// having a `function_body` dependency. - pub const DepType = enum { - /// The dependent references or uses the dependency's value, so must be - /// updated whenever it is changed. However, if the dependency is a - /// function and its type is unchanged, the dependent does not need to - /// be updated. - normal, - /// The dependent performs an inline or comptime call to the dependency, - /// or is a generic instantiation of it. It must therefore be updated - /// whenever the dependency is updated, even if the function type - /// remained the same. - function_body, - }; + /// Asserts that `zir_decl_index` is not `.none`. + fn getDeclaration(decl: Decl, zir: Zir) Zir.Inst.Declaration { + const zir_index = decl.zir_decl_index.unwrap().?; + const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; + return zir.extraData(Zir.Inst.Declaration, pl_node.payload_index).data; + } /// This name is relative to the containing namespace of the decl. /// The memory is owned by the containing File ZIR. @@ -497,10 +480,14 @@ pub const Decl = struct { } pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 { - assert(decl.zir_decl_index != .none); - const name_index = zir.extra[@intFromEnum(decl.zir_decl_index) + 5]; - if (name_index <= 1) return null; - return zir.nullTerminatedString(name_index); + return switch (decl.getDeclaration(zir).name) { + Zir.Inst.Declaration.comptime_name, + Zir.Inst.Declaration.usingnamespace_name, + Zir.Inst.Declaration.unnamed_test_name, + Zir.Inst.Declaration.decltest_name, + => null, + else => |name_index| zir.nullTerminatedString(name_index), + }; } pub fn contentsHash(decl: Decl, mod: *Module) std.zig.SrcHash { @@ -509,43 +496,22 @@ pub const Decl = struct { } pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash { - assert(decl.zir_decl_index != .none); - const hash_u32s = zir.extra[@intFromEnum(decl.zir_decl_index)..][0..4]; - const contents_hash = @as(std.zig.SrcHash, @bitCast(hash_u32s.*)); - return contents_hash; - } - - pub fn zirBlockIndex(decl: *const Decl, mod: *Module) Zir.Inst.Index { - assert(decl.zir_decl_index != .none); - const zir = decl.getFileScope(mod).zir; - return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 6]); - } - - pub fn zirAlignRef(decl: Decl, mod: *Module) Zir.Inst.Ref { - if (!decl.has_align) return .none; - assert(decl.zir_decl_index != .none); - const zir = decl.getFileScope(mod).zir; - return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 8]); - } - - pub fn zirLinksectionRef(decl: Decl, mod: *Module) Zir.Inst.Ref { - if (!decl.has_linksection_or_addrspace) return .none; - assert(decl.zir_decl_index != .none); - const zir = decl.getFileScope(mod).zir; - const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align); - return @enumFromInt(zir.extra[extra_index]); - } - - pub fn zirAddrspaceRef(decl: Decl, mod: *Module) Zir.Inst.Ref { - if (!decl.has_linksection_or_addrspace) return .none; - assert(decl.zir_decl_index != .none); - const zir = decl.getFileScope(mod).zir; - const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align) + 1; - return @enumFromInt(zir.extra[extra_index]); + const declaration = decl.getDeclaration(zir); + const src_hash_arr: [4]u32 = .{ + declaration.src_hash_0, + declaration.src_hash_1, + declaration.src_hash_2, + declaration.src_hash_3, + }; + return @bitCast(src_hash_arr); } - pub fn relativeToLine(decl: Decl, offset: u32) u32 { - return decl.src_line + offset; + pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies { + const zir = decl.getFileScope(zcu).zir; + const zir_index = decl.zir_decl_index.unwrap().?; + const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; + const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); + return extra.data.getBodies(@intCast(extra.end), zir); } pub fn relativeToNodeIndex(decl: Decl, offset: i32) Ast.Node.Index { @@ -3015,16 +2981,12 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { // The root decl will be null if the previous ZIR had AST errors. const root_decl = file.root_decl.unwrap() orelse return; - // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which - // creates a namespace, gets mapped from old to new here. + // Maps from old ZIR to new ZIR, declaration, struct_decl, enum_decl, etc. Any instruction which + // creates a namespace, and any `declaration` instruction, gets mapped from old to new here. var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; defer inst_map.deinit(gpa); - // Maps from old ZIR to new ZIR, the extra data index for the sub-decl item. - // e.g. the thing that Decl.zir_decl_index points to. - var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{}; - defer extra_map.deinit(gpa); - try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map); + try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map); // Walk the Decl graph, updating ZIR indexes, strings, and populating // the deleted and outdated lists. @@ -3050,7 +3012,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { // Anonymous decls should not be marked outdated. They will be re-generated // if their owner decl is marked outdated. if (decl.zir_decl_index.unwrap()) |old_zir_decl_index| { - const new_zir_decl_index = extra_map.get(old_zir_decl_index) orelse { + const new_zir_decl_index = inst_map.get(old_zir_decl_index) orelse { try file.deleted_decls.append(gpa, decl_index); continue; }; @@ -3098,9 +3060,9 @@ pub fn mapOldZirToNew( old_zir: Zir, new_zir: Zir, inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), - extra_map: *std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex), ) Allocator.Error!void { - // Contain ZIR indexes of declaration instructions. + // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc. + // Not `declaration`, as this does not create a namespace. const MatchedZirDecl = struct { old_inst: Zir.Inst.Index, new_inst: Zir.Inst.Index, @@ -3108,47 +3070,113 @@ pub fn mapOldZirToNew( var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{}; defer match_stack.deinit(gpa); - // Main struct inst is always the same + // Main struct inst is always matched try match_stack.append(gpa, .{ .old_inst = .main_struct_inst, .new_inst = .main_struct_inst, }); + // Used as temporary buffers for namespace declaration instructions var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa); defer old_decls.deinit(); var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa); defer new_decls.deinit(); while (match_stack.popOrNull()) |match_item| { + // Match the namespace declaration itself try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); - // Maps name to extra index of decl sub item. - var decl_map: std.StringHashMapUnmanaged(Zir.ExtraIndex) = .{}; - defer decl_map.deinit(gpa); + // Maps decl name to `declaration` instruction. + var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; + defer named_decls.deinit(gpa); + // Maps test name to `declaration` instruction. + var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; + defer named_tests.deinit(gpa); + // All unnamed tests, in order, for a best-effort match. + var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; + defer unnamed_tests.deinit(gpa); + // All comptime declarations, in order, for a best-effort match. + var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; + defer comptime_decls.deinit(gpa); + // All usingnamespace declarations, in order, for a best-effort match. + var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; + defer usingnamespace_decls.deinit(gpa); { var old_decl_it = old_zir.declIterator(match_item.old_inst); - while (old_decl_it.next()) |old_decl| { - try decl_map.put(gpa, old_decl.name, old_decl.sub_index); + while (old_decl_it.next()) |old_decl_inst| { + const old_decl, _ = old_zir.getDeclaration(old_decl_inst); + switch (old_decl.name) { + .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), + .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst), + .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst), + _ => { + const name_nts = old_decl.name.toString(old_zir).?; + const name = old_zir.nullTerminatedString(name_nts); + if (old_decl.name.isNamedTest(old_zir)) { + try named_tests.put(gpa, name, old_decl_inst); + } else { + try named_decls.put(gpa, name, old_decl_inst); + } + }, + } } } + var unnamed_test_idx: u32 = 0; + var comptime_decl_idx: u32 = 0; + var usingnamespace_decl_idx: u32 = 0; + var new_decl_it = new_zir.declIterator(match_item.new_inst); - while (new_decl_it.next()) |new_decl| { - const old_extra_index = decl_map.get(new_decl.name) orelse continue; - const new_extra_index = new_decl.sub_index; - try extra_map.put(gpa, old_extra_index, new_extra_index); - - try old_zir.findDecls(&old_decls, old_extra_index); - try new_zir.findDecls(&new_decls, new_extra_index); - var i: usize = 0; - while (true) : (i += 1) { - if (i >= old_decls.items.len) break; - if (i >= new_decls.items.len) break; - try match_stack.append(gpa, .{ - .old_inst = old_decls.items[i], - .new_inst = new_decls.items[i], - }); + while (new_decl_it.next()) |new_decl_inst| { + const new_decl, _ = new_zir.getDeclaration(new_decl_inst); + // Attempt to match this to a declaration in the old ZIR: + // * For named declarations (`const`/`var`/`fn`), we match based on name. + // * For named tests (`test "foo"`), we also match based on name. + // * For unnamed tests and decltests, we match based on order. + // * For comptime blocks, we match based on order. + // * For usingnamespace decls, we match based on order. + // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`. + const old_decl_inst = switch (new_decl.name) { + .@"comptime" => inst: { + if (comptime_decl_idx == comptime_decls.items.len) continue; + defer comptime_decl_idx += 1; + break :inst comptime_decls.items[comptime_decl_idx]; + }, + .@"usingnamespace" => inst: { + if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue; + defer usingnamespace_decl_idx += 1; + break :inst usingnamespace_decls.items[usingnamespace_decl_idx]; + }, + .unnamed_test, .decltest => inst: { + if (unnamed_test_idx == unnamed_tests.items.len) continue; + defer unnamed_test_idx += 1; + break :inst unnamed_tests.items[unnamed_test_idx]; + }, + _ => inst: { + const name_nts = new_decl.name.toString(old_zir).?; + const name = new_zir.nullTerminatedString(name_nts); + if (new_decl.name.isNamedTest(new_zir)) { + break :inst named_tests.get(name) orelse continue; + } else { + break :inst named_decls.get(name) orelse continue; + } + }, + }; + + // Match the `declaration` instruction + try inst_map.put(gpa, old_decl_inst, new_decl_inst); + + // Find namespace declarations within this declaration + try old_zir.findDecls(&old_decls, old_decl_inst); + try new_zir.findDecls(&new_decls, new_decl_inst); + + // We don't have any smart way of matching up these namespace declarations, so we always + // correlate them based on source order. + const n = @min(old_decls.items.len, new_decls.items.len); + try match_stack.ensureUnusedCapacity(gpa, n); + for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| { + match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst }); } } } @@ -3457,8 +3485,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { new_decl.src_line = 0; new_decl.is_pub = true; new_decl.is_exported = false; - new_decl.has_align = false; - new_decl.has_linksection_or_addrspace = false; new_decl.ty = Type.type; new_decl.alignment = .none; new_decl.@"linksection" = .none; @@ -3557,7 +3583,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const gpa = mod.gpa; const zir = decl.getFileScope(mod).zir; - const zir_datas = zir.instructions.items(.data); const builtin_type_target_index: InternPool.Index = blk: { const std_mod = mod.std_mod; @@ -3631,11 +3656,9 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { }; defer block_scope.instructions.deinit(gpa); - const zir_block_index = decl.zirBlockIndex(mod); - const inst_data = zir_datas[@intFromEnum(zir_block_index)].pl_node; - const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index); - const body = zir.extra[extra.end..][0..extra.data.body_len]; - const result_ref = (try sema.analyzeBodyBreak(&block_scope, @ptrCast(body))).?.operand; + const decl_bodies = decl.zirBodies(mod); + + const result_ref = (try sema.analyzeBodyBreak(&block_scope, decl_bodies.value_body)).?.operand; // We'll do some other bits with the Sema. Clear the type target index just // in case they analyze any type. sema.builtin_type_target_index = .none; @@ -3752,13 +3775,13 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { decl.ty = decl_tv.ty; decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); decl.alignment = blk: { - const align_ref = decl.zirAlignRef(mod); - if (align_ref == .none) break :blk .none; + const align_body = decl_bodies.align_body orelse break :blk .none; + const align_ref = (try sema.analyzeBodyBreak(&block_scope, align_body)).?.operand; break :blk try sema.resolveAlign(&block_scope, align_src, align_ref); }; decl.@"linksection" = blk: { - const linksection_ref = decl.zirLinksectionRef(mod); - if (linksection_ref == .none) break :blk .none; + const linksection_body = decl_bodies.linksection_body orelse break :blk .none; + const linksection_ref = (try sema.analyzeBodyBreak(&block_scope, linksection_body)).?.operand; const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, .{ .needed_comptime_reason = "linksection must be comptime-known", }); @@ -3778,15 +3801,15 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { }; const target = sema.mod.getTarget(); - break :blk switch (decl.zirAddrspaceRef(mod)) { - .none => switch (addrspace_ctx) { - .function => target_util.defaultAddressSpace(target, .function), - .variable => target_util.defaultAddressSpace(target, .global_mutable), - .constant => target_util.defaultAddressSpace(target, .global_constant), - else => unreachable, - }, - else => |addrspace_ref| try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx), + + const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { + .function => target_util.defaultAddressSpace(target, .function), + .variable => target_util.defaultAddressSpace(target, .global_mutable), + .constant => target_util.defaultAddressSpace(target, .global_constant), + else => unreachable, }; + const addrspace_ref = (try sema.analyzeBodyBreak(&block_scope, addrspace_body)).?.operand; + break :blk try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); }; decl.has_tv = true; decl.analysis = .complete; @@ -4125,52 +4148,32 @@ fn newEmbedFile( } pub fn scanNamespace( - mod: *Module, + zcu: *Zcu, namespace_index: Namespace.Index, - extra_start: usize, - decls_len: u32, + decls: []const Zir.Inst.Index, parent_decl: *Decl, -) Allocator.Error!usize { +) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = mod.gpa; - const namespace = mod.namespacePtr(namespace_index); - const zir = namespace.file_scope.zir; + const gpa = zcu.gpa; + const namespace = zcu.namespacePtr(namespace_index); - try mod.comp.work_queue.ensureUnusedCapacity(decls_len); - try namespace.decls.ensureTotalCapacity(gpa, decls_len); + try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); + try namespace.decls.ensureTotalCapacity(gpa, decls.len); - const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; - var extra_index = extra_start + bit_bags_count; - var bit_bag_index: usize = extra_start; - var cur_bit_bag: u32 = undefined; - var decl_i: u32 = 0; var scan_decl_iter: ScanDeclIter = .{ - .module = mod, + .zcu = zcu, .namespace_index = namespace_index, .parent_decl = parent_decl, }; - while (decl_i < decls_len) : (decl_i += 1) { - if (decl_i % 8 == 0) { - cur_bit_bag = zir.extra[bit_bag_index]; - bit_bag_index += 1; - } - const flags = @as(u4, @truncate(cur_bit_bag)); - cur_bit_bag >>= 4; - - const decl_sub_index = extra_index; - extra_index += 8; // src_hash(4) + line(1) + name(1) + value(1) + doc_comment(1) - extra_index += @as(u1, @truncate(flags >> 2)); // Align - extra_index += @as(u2, @as(u1, @truncate(flags >> 3))) * 2; // Link section or address space, consists of 2 Refs - - try scanDecl(&scan_decl_iter, decl_sub_index, flags); + for (decls) |decl_inst| { + try scanDecl(&scan_decl_iter, decl_inst); } - return extra_index; } const ScanDeclIter = struct { - module: *Module, + zcu: *Zcu, namespace_index: Namespace.Index, parent_decl: *Decl, usingnamespace_index: usize = 0, @@ -4178,119 +4181,112 @@ const ScanDeclIter = struct { unnamed_test_index: usize = 0, }; -fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Error!void { +fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); - const mod = iter.module; + const zcu = iter.zcu; const namespace_index = iter.namespace_index; - const namespace = mod.namespacePtr(namespace_index); - const gpa = mod.gpa; + const namespace = zcu.namespacePtr(namespace_index); + const gpa = zcu.gpa; const zir = namespace.file_scope.zir; - const ip = &mod.intern_pool; + const ip = &zcu.intern_pool; + + const pl_node = zir.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node; + const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); + const declaration = extra.data; - // zig fmt: off - const is_pub = (flags & 0b0001) != 0; - const export_bit = (flags & 0b0010) != 0; - const has_align = (flags & 0b0100) != 0; - const has_linksection_or_addrspace = (flags & 0b1000) != 0; - // zig fmt: on - - const line_off = zir.extra[decl_sub_index + 4]; - const line = iter.parent_decl.relativeToLine(line_off); - const decl_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[decl_sub_index + 5]); - const decl_doccomment_index = zir.extra[decl_sub_index + 7]; - const decl_zir_index = zir.extra[decl_sub_index + 6]; - const decl_block_inst_data = zir.instructions.items(.data)[decl_zir_index].pl_node; - const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node); + const line = iter.parent_decl.src_line + declaration.line_offset; + const decl_node = iter.parent_decl.relativeToNodeIndex(pl_node.src_node); // Every Decl needs a name. - var is_named_test = false; - var kind: Decl.Kind = .named; - const decl_name: InternPool.NullTerminatedString = switch (decl_name_index) { - .empty => name: { - if (export_bit) { - const i = iter.usingnamespace_index; - iter.usingnamespace_index += 1; - kind = .@"usingnamespace"; - break :name try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}); - } else { - const i = iter.comptime_index; - iter.comptime_index += 1; - kind = .@"comptime"; - break :name try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}); - } + const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { + .@"comptime" => info: { + const i = iter.comptime_index; + iter.comptime_index += 1; + break :info .{ + try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}), + .@"comptime", + false, + }; + }, + .@"usingnamespace" => info: { + const i = iter.usingnamespace_index; + iter.usingnamespace_index += 1; + break :info .{ + try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}), + .@"usingnamespace", + false, + }; }, - .unnamed_test_decl => name: { + .unnamed_test => info: { const i = iter.unnamed_test_index; iter.unnamed_test_index += 1; - kind = .@"test"; - break :name try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}); + break :info .{ + try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}), + .@"test", + false, + }; }, - .decltest => name: { - is_named_test = true; - const test_name = zir.nullTerminatedString(@enumFromInt(decl_doccomment_index)); - kind = .@"test"; - break :name try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{test_name}); + .decltest => info: { + assert(declaration.flags.has_doc_comment); + const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); + break :info .{ + try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{name}), + .@"test", + true, + }; }, - _ => name: { - const raw_name = zir.nullTerminatedString(decl_name_index); - if (raw_name.len == 0) { - is_named_test = true; - const test_name = zir.nullTerminatedString(@enumFromInt(@intFromEnum(decl_name_index) + 1)); - kind = .@"test"; - break :name try ip.getOrPutStringFmt(gpa, "test.{s}", .{test_name}); - } else { - break :name try ip.getOrPutString(gpa, raw_name); - } + _ => if (declaration.name.isNamedTest(zir)) .{ + try ip.getOrPutStringFmt(gpa, "test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), + .@"test", + true, + } else .{ + try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)), + .named, + false, }, }; - const is_exported = export_bit and decl_name_index != .empty; if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. const gop = try namespace.decls.getOrPutContextAdapted( gpa, decl_name, - DeclAdapter{ .mod = mod }, - Namespace.DeclContext{ .module = mod }, + DeclAdapter{ .mod = zcu }, + Namespace.DeclContext{ .module = zcu }, ); - const comp = mod.comp; + const comp = zcu.comp; if (!gop.found_existing) { - const new_decl_index = try mod.allocateNewDecl(namespace_index, decl_node, iter.parent_decl.src_scope); - const new_decl = mod.declPtr(new_decl_index); + const new_decl_index = try zcu.allocateNewDecl(namespace_index, decl_node, iter.parent_decl.src_scope); + const new_decl = zcu.declPtr(new_decl_index); new_decl.kind = kind; new_decl.name = decl_name; if (kind == .@"usingnamespace") { - namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub); + namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, declaration.flags.is_pub); } new_decl.src_line = line; gop.key_ptr.* = new_decl_index; // Exported decls, comptime decls, usingnamespace decls, and // test decls if in test mode, get analyzed. const decl_mod = namespace.file_scope.mod; - const want_analysis = is_exported or switch (decl_name_index) { - .empty => true, // comptime or usingnamespace decl - .unnamed_test_decl => blk: { - // test decl with no name. Skip the part where we check against - // the test name filter. - if (!comp.config.is_test) break :blk false; - if (decl_mod != mod.main_mod) break :blk false; - try mod.test_functions.put(gpa, new_decl_index, {}); - break :blk true; - }, - else => blk: { - if (!is_named_test) break :blk false; - if (!comp.config.is_test) break :blk false; - if (decl_mod != mod.main_mod) break :blk false; - if (comp.test_filter) |test_filter| { - if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) { - break :blk false; + const want_analysis = declaration.flags.is_export or switch (kind) { + .anon => unreachable, + .@"comptime", .@"usingnamespace" => true, + .named => false, + .@"test" => a: { + if (!comp.config.is_test) break :a false; + if (decl_mod != zcu.main_mod) break :a false; + if (is_named_test) { + if (comp.test_filter) |test_filter| { + if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) { + break :a false; + } } } - try mod.test_functions.put(gpa, new_decl_index, {}); - break :blk true; + try zcu.test_functions.put(gpa, new_decl_index, {}); + break :a true; }, }; if (want_analysis) { @@ -4299,46 +4295,42 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err }); comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); } - new_decl.is_pub = is_pub; - new_decl.is_exported = is_exported; - new_decl.has_align = has_align; - new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace; - new_decl.zir_decl_index = @enumFromInt(decl_sub_index); + new_decl.is_pub = declaration.flags.is_pub; + new_decl.is_exported = declaration.flags.is_export; + new_decl.zir_decl_index = decl_inst.toOptional(); new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive. return; } const decl_index = gop.key_ptr.*; - const decl = mod.declPtr(decl_index); + const decl = zcu.declPtr(decl_index); if (kind == .@"test") { const src_loc = SrcLoc{ - .file_scope = decl.getFileScope(mod), + .file_scope = decl.getFileScope(zcu), .parent_decl_node = decl.src_node, .lazy = .{ .token_offset = 1 }, }; const msg = try ErrorMsg.create(gpa, src_loc, "duplicate test name: {}", .{ - decl_name.fmt(&mod.intern_pool), + decl_name.fmt(ip), }); errdefer msg.destroy(gpa); - try mod.failed_decls.putNoClobber(gpa, decl_index, msg); + try zcu.failed_decls.putNoClobber(gpa, decl_index, msg); const other_src_loc = SrcLoc{ .file_scope = namespace.file_scope, .parent_decl_node = decl_node, .lazy = .{ .token_offset = 1 }, }; - try mod.errNoteNonLazy(other_src_loc, msg, "other test here", .{}); + try zcu.errNoteNonLazy(other_src_loc, msg, "other test here", .{}); } // Update the AST node of the decl; even if its contents are unchanged, it may // have been re-ordered. decl.src_node = decl_node; decl.src_line = line; - decl.is_pub = is_pub; - decl.is_exported = is_exported; + decl.is_pub = declaration.flags.is_pub; + decl.is_exported = declaration.flags.is_export; decl.kind = kind; - decl.has_align = has_align; - decl.has_linksection_or_addrspace = has_linksection_or_addrspace; - decl.zir_decl_index = @enumFromInt(decl_sub_index); - if (decl.getOwnedFunction(mod) != null) { + decl.zir_decl_index = decl_inst.toOptional(); + if (decl.getOwnedFunction(zcu) != null) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); @@ -4718,8 +4710,6 @@ pub fn allocateNewDecl( .generation = 0, .is_pub = false, .is_exported = false, - .has_linksection_or_addrspace = false, - .has_align = false, .alive = false, .kind = .anon, }); diff --git a/src/Sema.zig b/src/Sema.zig index 44a04e7d256c..eac66f2ad1d8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1223,6 +1223,10 @@ fn analyzeBodyInner( .trap => break sema.zirTrap(block, inst), // zig fmt: on + // This instruction never exists in an analyzed body. It exists only in the declaration + // list for a container type. + .declaration => unreachable, + .extended => ext: { const extended = datas[@intFromEnum(inst)].extended; break :ext switch (extended.opcode) { @@ -2692,7 +2696,9 @@ pub fn getStructType( } } - extra_index = try mod.scanNamespace(namespace, extra_index, decls_len, mod.declPtr(decl)); + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(namespace, decls, mod.declPtr(decl)); + extra_index += decls_len; const ty = try ip.getStructType(gpa, .{ .decl = decl, @@ -2929,7 +2935,9 @@ fn zirEnumDecl( const new_namespace = mod.namespacePtr(new_namespace_index); errdefer if (!done) mod.destroyNamespace(new_namespace_index); - extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(new_namespace_index, decls, new_decl); + extra_index += decls_len; const body = sema.code.bodySlice(extra_index, body_len); extra_index += body.len; @@ -3219,7 +3227,8 @@ fn zirUnionDecl( new_decl.val = Value.fromInterned(union_ty); new_namespace.ty = Type.fromInterned(union_ty); - _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(new_namespace_index, decls, new_decl); const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); @@ -3282,7 +3291,8 @@ fn zirOpaqueDecl( new_decl.val = Value.fromInterned(opaque_ty); new_namespace.ty = Type.fromInterned(opaque_ty); - extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(new_namespace_index, decls, new_decl); const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); @@ -36252,9 +36262,7 @@ fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { } // Skip over decls. - var decls_it = zir.declIteratorInner(extra_index, decls_len); - while (decls_it.next()) |_| {} - extra_index = decls_it.extra_index; + extra_index += decls_len; return .{ fields_len, small, extra_index }; } @@ -36715,9 +36723,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un } else 0; // Skip over decls. - var decls_it = zir.declIteratorInner(extra_index, decls_len); - while (decls_it.next()) |_| {} - extra_index = decls_it.extra_index; + extra_index += decls_len; const body = zir.bodySlice(extra_index, body_len); extra_index += body.len; @@ -37710,10 +37716,12 @@ pub fn analyzeAddressSpace( ctx: AddressSpaceContext, ) !std.builtin.AddressSpace { const mod = sema.mod; - const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, .{ + const air_ref = try sema.resolveInst(zir_ref); + const coerced = try sema.coerce(block, Type.fromInterned(.address_space_type), air_ref, src); + const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .needed_comptime_reason = "address space must be comptime-known", }); - const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); + const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_val); const target = sema.mod.getTarget(); const arch = target.cpu.arch; diff --git a/src/Zir.zig b/src/Zir.zig index 3737467e47be..4462083b1f72 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -30,7 +30,7 @@ instructions: std.MultiArrayList(Inst).Slice, /// is referencing the data here whether they want to store both index and length, /// thus allowing null bytes, or store only index, and use null-termination. The /// `string_bytes` array is agnostic to either usage. -/// Indexes 0 and 1 are reserved for special cases. +/// Index 0 is reserved for special cases. string_bytes: []u8, /// The meaning of this data is determined by `Inst.Tag` value. /// The first few indexes are reserved. See `ExtraIndex` for the values. @@ -60,21 +60,6 @@ pub const ExtraIndex = enum(u32) { imports, _, - - pub fn toOptional(i: ExtraIndex) OptionalExtraIndex { - return @enumFromInt(@intFromEnum(i)); - } -}; - -pub const OptionalExtraIndex = enum(u32) { - compile_errors, - imports, - none = std.math.maxInt(u32), - _, - - pub fn unwrap(oi: OptionalExtraIndex) ?ExtraIndex { - return if (oi == .none) null else @enumFromInt(@intFromEnum(oi)); - } }; fn ExtraData(comptime T: type) type { @@ -93,6 +78,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) { Inst.Ref, Inst.Index, + Inst.Declaration.Name, NullTerminatedString, => @enumFromInt(code.extra[i]), @@ -102,6 +88,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) { Inst.SwitchBlock.Bits, Inst.SwitchBlockErrUnion.Bits, Inst.FuncFancy.Bits, + Inst.Declaration.Flags, => @bitCast(code.extra[i]), else => @compileError("bad field type"), @@ -116,8 +103,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) { pub const NullTerminatedString = enum(u32) { empty = 0, - unnamed_test_decl = 1, - decltest = 2, _, }; @@ -304,6 +289,12 @@ pub const Inst = struct { /// a noreturn instruction. /// Uses the `pl_node` union field. Payload is `Block`. block_inline, + /// This instruction may only ever appear in the list of declarations for a + /// namespace type, e.g. within a `struct_decl` instruction. It represents a + /// single source declaration (`const`/`var`/`fn`), containing the name, + /// attributes, type, and value of the declaration. + /// Uses the `pl_node` union field. Payload is `Declaration`. + declaration, /// Implements `suspend {...}`. /// Uses the `pl_node` union field. Payload is `Block`. suspend_block, @@ -1092,6 +1083,7 @@ pub const Inst = struct { .block, .block_comptime, .block_inline, + .declaration, .suspend_block, .loop, .bool_br_and, @@ -1405,6 +1397,7 @@ pub const Inst = struct { .block, .block_comptime, .block_inline, + .declaration, .suspend_block, .loop, .bool_br_and, @@ -1639,6 +1632,7 @@ pub const Inst = struct { .block = .pl_node, .block_comptime = .pl_node, .block_inline = .pl_node, + .declaration = .pl_node, .suspend_block = .pl_node, .bool_not = .un_node, .bool_br_and = .bool_br, @@ -2508,6 +2502,7 @@ pub const Inst = struct { /// If this is 1 it means return_type is a simple Ref ret_body_len: u32, /// Points to the block that contains the param instructions for this function. + /// If this is a `declaration`, it refers to the declaration's value body. param_block: Index, body_len: u32, @@ -2565,6 +2560,7 @@ pub const Inst = struct { /// 18. src_locs: Func.SrcLocs // if body_len != 0 pub const FuncFancy = struct { /// Points to the block that contains the param instructions for this function. + /// If this is a `declaration`, it refers to the declaration's value body. param_block: Index, body_len: u32, bits: Bits, @@ -2632,6 +2628,116 @@ pub const Inst = struct { body_len: u32, }; + /// Trailing: + /// 0. doc_comment: u32 // if `has_doc_comment`; null-terminated string index + /// 1. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align` + /// 2. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection` + /// 3. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace` + /// 4. value_body_inst: Zir.Inst.Index + /// - for each `value_body_len` + /// - body to be exited via `break_inline` to this `declaration` instruction + /// 5. align_body_inst: Zir.Inst.Index + /// - for each `align_body_len` + /// - body to be exited via `break_inline` to this `declaration` instruction + /// 6. linksection_body_inst: Zir.Inst.Index + /// - for each `linksection_body_len` + /// - body to be exited via `break_inline` to this `declaration` instruction + /// 7. addrspace_body_inst: Zir.Inst.Index + /// - for each `addrspace_body_len` + /// - body to be exited via `break_inline` to this `declaration` instruction + pub const Declaration = struct { + // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. + src_hash_0: u32, + src_hash_1: u32, + src_hash_2: u32, + src_hash_3: u32, + /// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc. + name: Name, + /// This Decl's line number relative to that of its parent. + /// TODO: column must be encoded similarly to respect non-formatted code! + line_offset: u32, + flags: Flags, + + pub const Flags = packed struct(u32) { + value_body_len: u28, + is_pub: bool, + is_export: bool, + has_doc_comment: bool, + has_align_linksection_addrspace: bool, + }; + + pub const Name = enum(u32) { + @"comptime" = std.math.maxInt(u32), + @"usingnamespace" = std.math.maxInt(u32) - 1, + unnamed_test = std.math.maxInt(u32) - 2, + /// In this case, `has_doc_comment` will be true, and the doc + /// comment body is the identifier name. + decltest = std.math.maxInt(u32) - 3, + /// Other values are `NullTerminatedString` values, i.e. index into + /// `string_bytes`. If the byte referenced is 0, the decl is a named + /// test, and the actual name begins at the following byte. + _, + + pub fn isNamedTest(name: Name, zir: Zir) bool { + return switch (name) { + .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => false, + _ => zir.string_bytes[@intFromEnum(name)] == 0, + }; + } + pub fn toString(name: Name, zir: Zir) ?NullTerminatedString { + switch (name) { + .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => return null, + _ => {}, + } + const idx: u32 = @intFromEnum(name); + if (zir.string_bytes[idx] == 0) { + // Named test + return @enumFromInt(idx + 1); + } + return @enumFromInt(idx); + } + }; + + pub const Bodies = struct { + value_body: []const Index, + align_body: ?[]const Index, + linksection_body: ?[]const Index, + addrspace_body: ?[]const Index, + }; + + pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies { + var extra_index: u32 = extra_end; + extra_index += @intFromBool(declaration.flags.has_doc_comment); + const value_body_len = declaration.flags.value_body_len; + const align_body_len, const linksection_body_len, const addrspace_body_len = lens: { + if (!declaration.flags.has_align_linksection_addrspace) { + break :lens .{ 0, 0, 0 }; + } + const lens = zir.extra[extra_index..][0..3].*; + extra_index += 3; + break :lens lens; + }; + return .{ + .value_body = b: { + defer extra_index += value_body_len; + break :b zir.bodySlice(extra_index, value_body_len); + }, + .align_body = if (align_body_len == 0) null else b: { + defer extra_index += align_body_len; + break :b zir.bodySlice(extra_index, align_body_len); + }, + .linksection_body = if (linksection_body_len == 0) null else b: { + defer extra_index += linksection_body_len; + break :b zir.bodySlice(extra_index, linksection_body_len); + }, + .addrspace_body = if (addrspace_body_len == 0) null else b: { + defer extra_index += addrspace_body_len; + break :b zir.bodySlice(extra_index, addrspace_body_len); + }, + }; + } + }; + /// Stored inside extra, with trailing arguments according to `args_len`. /// Implicit 0. arg_0_start: u32, // always same as `args_len` /// 1. arg_end: u32, // for each `args_len` @@ -2913,37 +3019,14 @@ pub const Inst = struct { /// 3. backing_int_body_len: u32, // if has_backing_int /// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0 /// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0 - /// 6. decl_bits: u32 // for every 8 decls - /// - sets of 4 bits: - /// 0b000X: whether corresponding decl is pub - /// 0b00X0: whether corresponding decl is exported - /// 0b0X00: whether corresponding decl has an align expression - /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 7. decl: { // for every decls_len - /// src_hash: [4]u32, // hash of source bytes - /// line: u32, // line number of decl, relative to parent - /// name: NullTerminatedString, // null terminated string index - /// - 0 means comptime or usingnamespace decl. - /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace - /// - 1 means test decl with no name. - /// - 2 means that the test is a decltest, doc_comment gives the name of the identifier - /// - if there is a 0 byte at the position `name` indexes, it indicates - /// this is a test decl, and the name starts at `name+1`. - /// value: Index, - /// doc_comment: u32, .empty if no doc comment, if this is a decltest, doc_comment references the decl name in the string table - /// align: Ref, // if corresponding bit is set - /// link_section_or_address_space: { // if corresponding bit is set. - /// link_section: Ref, - /// address_space: Ref, - /// } - /// } - /// 8. flags: u32 // for every 8 fields + /// 6. decl: Index, // for every decls_len; points to a `declaration` instruction + /// 7. flags: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has an align expression /// 0b00X0: whether corresponding field has a default expression /// 0b0X00: whether corresponding field is comptime /// 0bX000: whether corresponding field has a type expression - /// 9. fields: { // for every fields_len + /// 8. fields: { // for every fields_len /// field_name: u32, // if !is_tuple /// doc_comment: NullTerminatedString, // .empty if no doc comment /// field_type: Ref, // if corresponding bit is not set. none means anytype. @@ -3009,33 +3092,11 @@ pub const Inst = struct { /// 2. body_len: u32, // if has_body_len /// 3. fields_len: u32, // if has_fields_len /// 4. decls_len: u32, // if has_decls_len - /// 5. decl_bits: u32 // for every 8 decls - /// - sets of 4 bits: - /// 0b000X: whether corresponding decl is pub - /// 0b00X0: whether corresponding decl is exported - /// 0b0X00: whether corresponding decl has an align expression - /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 6. decl: { // for every decls_len - /// src_hash: [4]u32, // hash of source bytes - /// line: u32, // line number of decl, relative to parent - /// name: NullTerminatedString, // null terminated string index - /// - 0 means comptime or usingnamespace decl. - /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace - /// - 1 means test decl with no name. - /// - if there is a 0 byte at the position `name` indexes, it indicates - /// this is a test decl, and the name starts at `name+1`. - /// value: Index, - /// doc_comment: u32, // .empty if no doc_comment - /// align: Ref, // if corresponding bit is set - /// link_section_or_address_space: { // if corresponding bit is set. - /// link_section: Ref, - /// address_space: Ref, - /// } - /// } - /// 7. inst: Index // for every body_len - /// 8. has_bits: u32 // for every 32 fields + /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction + /// 6. inst: Index // for every body_len + /// 7. has_bits: u32 // for every 32 fields /// - the bit is whether corresponding field has an value expression - /// 9. fields: { // for every fields_len + /// 8. fields: { // for every fields_len /// field_name: u32, /// doc_comment: u32, // .empty if no doc_comment /// value: Ref, // if corresponding bit is set @@ -3059,37 +3120,15 @@ pub const Inst = struct { /// 2. body_len: u32, // if has_body_len /// 3. fields_len: u32, // if has_fields_len /// 4. decls_len: u32, // if has_decls_len - /// 5. decl_bits: u32 // for every 8 decls - /// - sets of 4 bits: - /// 0b000X: whether corresponding decl is pub - /// 0b00X0: whether corresponding decl is exported - /// 0b0X00: whether corresponding decl has an align expression - /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 6. decl: { // for every decls_len - /// src_hash: [4]u32, // hash of source bytes - /// line: u32, // line number of decl, relative to parent - /// name: NullTerminatedString, // null terminated string index - /// - 0 means comptime or usingnamespace decl. - /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace - /// - 1 means test decl with no name. - /// - if there is a 0 byte at the position `name` indexes, it indicates - /// this is a test decl, and the name starts at `name+1`. - /// value: Index, - /// doc_comment: NullTerminatedString, // .empty if no doc comment - /// align: Ref, // if corresponding bit is set - /// link_section_or_address_space: { // if corresponding bit is set. - /// link_section: Ref, - /// address_space: Ref, - /// } - /// } - /// 7. inst: Index // for every body_len - /// 8. has_bits: u32 // for every 8 fields + /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction + /// 6. inst: Index // for every body_len + /// 7. has_bits: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has a type expression /// 0b00X0: whether corresponding field has a align expression /// 0b0X00: whether corresponding field has a tag value expression /// 0bX000: unused - /// 9. fields: { // for every fields_len + /// 8. fields: { // for every fields_len /// field_name: NullTerminatedString, // null terminated string index /// doc_comment: NullTerminatedString, // .empty if no doc comment /// field_type: Ref, // if corresponding bit is set @@ -3121,29 +3160,7 @@ pub const Inst = struct { /// Trailing: /// 0. src_node: i32, // if has_src_node /// 1. decls_len: u32, // if has_decls_len - /// 2. decl_bits: u32 // for every 8 decls - /// - sets of 4 bits: - /// 0b000X: whether corresponding decl is pub - /// 0b00X0: whether corresponding decl is exported - /// 0b0X00: whether corresponding decl has an align expression - /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 3. decl: { // for every decls_len - /// src_hash: [4]u32, // hash of source bytes - /// line: u32, // line number of decl, relative to parent - /// name: NullTerminatedString, // null terminated string index - /// - 0 means comptime or usingnamespace decl. - /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace - /// - 1 means test decl with no name. - /// - if there is a 0 byte at the position `name` indexes, it indicates - /// this is a test decl, and the name starts at `name+1`. - /// value: Index, - /// doc_comment: NullTerminatedString, // .empty if no doc comment, - /// align: Ref, // if corresponding bit is set - /// link_section_or_address_space: { // if corresponding bit is set. - /// link_section: Ref, - /// address_space: Ref, - /// } - /// } + /// 2. decl: Index, // for every decls_len; points to a `declaration` instruction pub const OpaqueDecl = struct { pub const Small = packed struct { has_src_node: bool, @@ -3407,44 +3424,17 @@ pub const Inst = struct { pub const SpecialProng = enum { none, @"else", under }; pub const DeclIterator = struct { - extra_index: usize, - bit_bag_index: usize, - cur_bit_bag: u32, - decl_i: u32, - decls_len: u32, + extra_index: u32, + decls_remaining: u32, zir: Zir, - pub const Item = struct { - name: [:0]const u8, - sub_index: ExtraIndex, - flags: u4, - }; - - pub fn next(it: *DeclIterator) ?Item { - if (it.decl_i >= it.decls_len) return null; - - if (it.decl_i % 8 == 0) { - it.cur_bit_bag = it.zir.extra[it.bit_bag_index]; - it.bit_bag_index += 1; - } - it.decl_i += 1; - - const flags: u4 = @truncate(it.cur_bit_bag); - it.cur_bit_bag >>= 4; - - const sub_index: ExtraIndex = @enumFromInt(it.extra_index); - it.extra_index += 5; // src_hash(4) + line(1) - const name = it.zir.nullTerminatedString(@enumFromInt(it.zir.extra[it.extra_index])); - it.extra_index += 3; // name(1) + value(1) + doc_comment(1) - it.extra_index += @as(u1, @truncate(flags >> 2)); // align - it.extra_index += @as(u1, @truncate(flags >> 3)); // link_section - it.extra_index += @as(u1, @truncate(flags >> 3)); // address_space - - return Item{ - .sub_index = sub_index, - .name = name, - .flags = flags, - }; + pub fn next(it: *DeclIterator) ?Inst.Index { + if (it.decls_remaining == 0) return null; + const decl_inst: Zir.Inst.Index = @enumFromInt(it.zir.extra[it.extra_index]); + it.extra_index += 1; + it.decls_remaining -= 1; + assert(it.zir.instructions.items(.tag)[@intFromEnum(decl_inst)] == .declaration); + return decl_inst; } }; @@ -3454,14 +3444,18 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { switch (tags[@intFromEnum(decl_inst)]) { // Functions are allowed and yield no iterations. // There is one case matching this in the extended instruction set below. - .func, .func_inferred, .func_fancy => return declIteratorInner(zir, 0, 0), + .func, .func_inferred, .func_fancy => return .{ + .extra_index = undefined, + .decls_remaining = 0, + .zir = zir, + }, .extended => { const extended = datas[@intFromEnum(decl_inst)].extended; switch (extended.opcode) { .struct_decl => { const small: Inst.StructDecl.Small = @bitCast(extended.small); - var extra_index: usize = extended.operand; + var extra_index: u32 = extended.operand; extra_index += @intFromBool(small.has_src_node); extra_index += @intFromBool(small.has_fields_len); const decls_len = if (small.has_decls_len) decls_len: { @@ -3480,11 +3474,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { } } - return declIteratorInner(zir, extra_index, decls_len); + return .{ + .extra_index = extra_index, + .decls_remaining = decls_len, + .zir = zir, + }; }, .enum_decl => { const small: Inst.EnumDecl.Small = @bitCast(extended.small); - var extra_index: usize = extended.operand; + var extra_index: u32 = extended.operand; extra_index += @intFromBool(small.has_src_node); extra_index += @intFromBool(small.has_tag_type); extra_index += @intFromBool(small.has_body_len); @@ -3495,11 +3493,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { break :decls_len decls_len; } else 0; - return declIteratorInner(zir, extra_index, decls_len); + return .{ + .extra_index = extra_index, + .decls_remaining = decls_len, + .zir = zir, + }; }, .union_decl => { const small: Inst.UnionDecl.Small = @bitCast(extended.small); - var extra_index: usize = extended.operand; + var extra_index: u32 = extended.operand; extra_index += @intFromBool(small.has_src_node); extra_index += @intFromBool(small.has_tag_type); extra_index += @intFromBool(small.has_body_len); @@ -3510,11 +3512,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { break :decls_len decls_len; } else 0; - return declIteratorInner(zir, extra_index, decls_len); + return .{ + .extra_index = extra_index, + .decls_remaining = decls_len, + .zir = zir, + }; }, .opaque_decl => { const small: Inst.OpaqueDecl.Small = @bitCast(extended.small); - var extra_index: usize = extended.operand; + var extra_index: u32 = extended.operand; extra_index += @intFromBool(small.has_src_node); const decls_len = if (small.has_decls_len) decls_len: { const decls_len = zir.extra[extra_index]; @@ -3522,7 +3528,11 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { break :decls_len decls_len; } else 0; - return declIteratorInner(zir, extra_index, decls_len); + return .{ + .extra_index = extra_index, + .decls_remaining = decls_len, + .zir = zir, + }; }, else => unreachable, } @@ -3531,25 +3541,17 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { } } -pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclIterator { - const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; - return .{ - .zir = zir, - .extra_index = extra_index + bit_bags_count, - .bit_bag_index = extra_index, - .cur_bit_bag = undefined, - .decl_i = 0, - .decls_len = decls_len, - }; -} - /// The iterator would have to allocate memory anyway to iterate. So here we populate /// an ArrayList as the result. -pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: ExtraIndex) !void { - const block_inst: Zir.Inst.Index = @enumFromInt(zir.extra[@intFromEnum(decl_sub_index) + 6]); +pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void { list.clearRetainingCapacity(); + const declaration, const extra_end = zir.getDeclaration(decl_inst); + const bodies = declaration.getBodies(extra_end, zir); - return zir.findDeclsInner(list, block_inst); + try zir.findDeclsBody(list, bodies.value_body); + if (bodies.align_body) |b| try zir.findDeclsBody(list, b); + if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b); + if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b); } fn findDeclsInner( @@ -3791,8 +3793,17 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index { else => unreachable, }; - const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index); - return zir.bodySlice(param_block.end, param_block.data.body_len); + switch (tags[@intFromEnum(param_block_index)]) { + .block, .block_comptime, .block_inline => { + const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index); + return zir.bodySlice(param_block.end, param_block.data.body_len); + }, + .declaration => { + const decl, const extra_end = zir.getDeclaration(param_block_index); + return decl.getBodies(extra_end, zir).value_body; + }, + else => unreachable, + } } pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { @@ -3888,12 +3899,17 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { }, else => unreachable, }; - switch (tags[@intFromEnum(info.param_block)]) { - .block, .block_comptime, .block_inline => {}, // OK - else => unreachable, // assertion failure - } - const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index); - const param_body = zir.bodySlice(param_block.end, param_block.data.body_len); + const param_body = switch (tags[@intFromEnum(info.param_block)]) { + .block, .block_comptime, .block_inline => param_body: { + const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index); + break :param_body zir.bodySlice(param_block.end, param_block.data.body_len); + }, + .declaration => param_body: { + const decl, const extra_end = zir.getDeclaration(info.param_block); + break :param_body decl.getBodies(extra_end, zir).value_body; + }, + else => unreachable, + }; var total_params_len: u32 = 0; for (param_body) |inst| { switch (tags[@intFromEnum(inst)]) { @@ -3912,3 +3928,13 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .total_params_len = total_params_len, }; } + +pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } { + assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration); + const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node; + const extra = zir.extraData(Inst.Declaration, pl_node.payload_index); + return .{ + extra.data, + @intCast(extra.end), + }; +} diff --git a/src/main.zig b/src/main.zig index fd650384f913..211f2b461aa9 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6856,10 +6856,7 @@ pub fn cmdChangelist( var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; defer inst_map.deinit(gpa); - var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{}; - defer extra_map.deinit(gpa); - - try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map); + try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map); var bw = io.bufferedWriter(io.getStdOut().writer()); const stdout = bw.writer(); @@ -6868,16 +6865,8 @@ pub fn cmdChangelist( var it = inst_map.iterator(); while (it.next()) |entry| { try stdout.print(" %{d} => %{d}\n", .{ - entry.key_ptr.*, entry.value_ptr.*, - }); - } - } - { - try stdout.print("Extra mappings:\n", .{}); - var it = extra_map.iterator(); - while (it.next()) |entry| { - try stdout.print(" {d} => {d}\n", .{ - entry.key_ptr.*, entry.value_ptr.*, + @intFromEnum(entry.key_ptr.*), + @intFromEnum(entry.value_ptr.*), }); } } diff --git a/src/print_zir.zig b/src/print_zir.zig index a6e3ca91a89d..32904d3a0aac 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -521,6 +521,8 @@ const Writer = struct { .@"defer" => try self.writeDefer(stream, inst), .defer_err_code => try self.writeDeferErrCode(stream, inst), + .declaration => try self.writeDeclaration(stream, inst), + .extended => try self.writeExtended(stream, inst), } } @@ -1454,8 +1456,9 @@ const Writer = struct { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra_index); + try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); self.indent -= 2; + extra_index += decls_len; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } @@ -1634,8 +1637,9 @@ const Writer = struct { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra_index); + try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); self.indent -= 2; + extra_index += decls_len; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}"); } @@ -1727,124 +1731,6 @@ const Writer = struct { try self.writeSrcNode(stream, src_node); } - fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize { - const parent_decl_node = self.parent_decl_node; - const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; - var extra_index = extra_start + bit_bags_count; - var bit_bag_index: usize = extra_start; - var cur_bit_bag: u32 = undefined; - var decl_i: u32 = 0; - while (decl_i < decls_len) : (decl_i += 1) { - if (decl_i % 8 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const is_pub = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const is_exported = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_section_or_addrspace = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - const sub_index = extra_index; - - const hash_u32s = self.code.extra[extra_index..][0..4]; - extra_index += 4; - const line = self.code.extra[extra_index]; - extra_index += 1; - const decl_name_index = self.code.extra[extra_index]; - extra_index += 1; - const decl_index: Zir.Inst.Index = @enumFromInt(self.code.extra[extra_index]); - extra_index += 1; - const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); - extra_index += 1; - - const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { - const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :inst inst; - }; - const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { - const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :inst inst; - }; - const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { - const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :inst inst; - }; - - const pub_str = if (is_pub) "pub " else ""; - const hash_bytes: [16]u8 = @bitCast(hash_u32s.*); - if (decl_name_index == 0) { - try stream.writeByteNTimes(' ', self.indent); - const name = if (is_exported) "usingnamespace" else "comptime"; - try stream.writeAll(pub_str); - try stream.writeAll(name); - } else if (decl_name_index == 1) { - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("test"); - } else if (decl_name_index == 2) { - try stream.writeByteNTimes(' ', self.indent); - try stream.print("[{d}] decltest {s}", .{ sub_index, self.code.nullTerminatedString(doc_comment_index) }); - } else { - const raw_decl_name = self.code.nullTerminatedString(@enumFromInt(decl_name_index)); - const decl_name = if (raw_decl_name.len == 0) - self.code.nullTerminatedString(@enumFromInt(decl_name_index + 1)) - else - raw_decl_name; - const test_str = if (raw_decl_name.len == 0) "test \"" else ""; - const export_str = if (is_exported) "export " else ""; - - try self.writeDocComment(stream, doc_comment_index); - - try stream.writeByteNTimes(' ', self.indent); - const endquote_if_test: []const u8 = if (raw_decl_name.len == 0) "\"" else ""; - try stream.print("[{d}] {s}{s}{s}{}{s}", .{ - sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), endquote_if_test, - }); - if (align_inst != .none) { - try stream.writeAll(" align("); - try self.writeInstRef(stream, align_inst); - try stream.writeAll(")"); - } - if (addrspace_inst != .none) { - try stream.writeAll(" addrspace("); - try self.writeInstRef(stream, addrspace_inst); - try stream.writeAll(")"); - } - if (section_inst != .none) { - try stream.writeAll(" linksection("); - try self.writeInstRef(stream, section_inst); - try stream.writeAll(")"); - } - } - - if (self.recurse_decls) { - const tag = self.code.instructions.items(.tag)[@intFromEnum(decl_index)]; - try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ - line, std.fmt.fmtSliceHexLower(&hash_bytes), @intFromEnum(decl_index), @tagName(tag), - }); - - const decl_block_inst_data = self.code.instructions.items(.data)[@intFromEnum(decl_index)].pl_node; - const sub_decl_node_off = decl_block_inst_data.src_node; - self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); - try self.writePlNodeBlockWithoutSrc(stream, decl_index); - self.parent_decl_node = parent_decl_node; - try self.writeSrc(stream, decl_block_inst_data.src()); - try stream.writeAll("\n"); - } else { - try stream.print(" line({d}) hash({}): %{d} = ...\n", .{ - line, std.fmt.fmtSliceHexLower(&hash_bytes), @intFromEnum(decl_index), - }); - } - } - return extra_index; - } - fn writeEnumDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small)); var extra_index: usize = extended.operand; @@ -1891,8 +1777,9 @@ const Writer = struct { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra_index); + try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); self.indent -= 2; + extra_index += decls_len; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } @@ -1988,7 +1875,7 @@ const Writer = struct { try stream.writeAll("{\n"); self.indent += 2; - _ = try self.writeDecls(stream, decls_len, extra_index); + try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("})"); @@ -2762,6 +2649,64 @@ const Writer = struct { try stream.writeByte(')'); } + fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; + const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index); + const doc_comment: ?Zir.NullTerminatedString = if (extra.data.flags.has_doc_comment) dc: { + break :dc @enumFromInt(self.code.extra[extra.end]); + } else null; + if (extra.data.flags.is_pub) try stream.writeAll("pub "); + if (extra.data.flags.is_export) try stream.writeAll("export "); + switch (extra.data.name) { + .@"comptime" => try stream.writeAll("comptime"), + .@"usingnamespace" => try stream.writeAll("usingnamespace"), + .unnamed_test => try stream.writeAll("test"), + .decltest => try stream.print("decltest '{s}'", .{self.code.nullTerminatedString(doc_comment.?)}), + _ => { + const name = extra.data.name.toString(self.code).?; + const prefix = if (extra.data.name.isNamedTest(self.code)) "test " else ""; + try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) }); + }, + } + const src_hash_arr: [4]u32 = .{ + extra.data.src_hash_0, + extra.data.src_hash_1, + extra.data.src_hash_2, + extra.data.src_hash_3, + }; + const src_hash_bytes: [16]u8 = @bitCast(src_hash_arr); + try stream.print(" line(+{d}) hash({})", .{ extra.data.line_offset, std.fmt.fmtSliceHexLower(&src_hash_bytes) }); + + { + const prev_parent_decl_node = self.parent_decl_node; + defer self.parent_decl_node = prev_parent_decl_node; + self.parent_decl_node = self.relativeToNodeIndex(inst_data.src_node); + + const bodies = extra.data.getBodies(@intCast(extra.end), self.code); + + try stream.writeAll(" value="); + try self.writeBracedDecl(stream, bodies.value_body); + + if (bodies.align_body) |b| { + try stream.writeAll(" align="); + try self.writeBracedDecl(stream, b); + } + + if (bodies.linksection_body) |b| { + try stream.writeAll(" linksection="); + try self.writeBracedDecl(stream, b); + } + + if (bodies.addrspace_body) |b| { + try stream.writeAll(" addrspace="); + try self.writeBracedDecl(stream, b); + } + } + + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void { if (ref == .none) { return stream.writeAll(".none");