Merge pull request #18654 from mlugg/incremental-the-second

InternPool: introduce TrackedInst to prepare for incremental compilation
This commit is contained in:
Andrew Kelley 2024-01-26 15:51:33 -08:00 committed by GitHub
commit b96fb858c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 964 additions and 1053 deletions

View File

@ -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 = &section_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);
_ = try decl_gz.addBreak(.break_inline, decl_inst, func_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 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);
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);
}
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 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);
_ = try decl_block.addBreak(.break_inline, decl_inst, namespace_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 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);
_ = try decl_block.addBreak(.break_inline, decl_inst, func_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 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();
}

View File

@ -2846,22 +2846,14 @@ fn walkInstruction(
return res;
},
.block_inline => {
return self.walkRef(
const pl_node = data[@intFromEnum(inst)].pl_node;
const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
return self.walkInlineBody(
file,
parent_scope,
try self.srcLocInfo(file, pl_node.src_node, parent_src),
parent_src,
getBlockInlineBreak(file.zir, inst) orelse {
const res = DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.type_type) },
.expr = .{ .comptimeExpr = self.comptime_exprs.items.len },
};
const pl_node = data[@intFromEnum(inst)].pl_node;
const block_inline_expr = try self.getBlockSource(file, parent_src, pl_node.src_node);
try self.comptime_exprs.append(self.arena, .{
.code = block_inline_expr,
});
return res;
},
file.zir.bodySlice(extra.end, extra.data.body_len),
need_type,
call_ctx,
);
@ -4084,19 +4076,11 @@ fn analyzeAllDecls(
// First loop to discover decl names
{
var it = original_it;
while (it.next()) |d| {
const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 5]);
switch (decl_name_index) {
.empty,
.unnamed_test_decl,
.decltest,
=> continue,
_ => if (file.zir.nullTerminatedString(decl_name_index).len == 0) {
continue;
},
}
try scope.insertDeclRef(self.arena, decl_name_index, .Pending);
while (it.next()) |zir_index| {
const declaration, _ = file.zir.getDeclaration(zir_index);
if (declaration.name.isNamedTest(file.zir)) continue;
const decl_name = declaration.name.toString(file.zir) orelse continue;
try scope.insertDeclRef(self.arena, decl_name, .Pending);
}
}
@ -4104,147 +4088,114 @@ fn analyzeAllDecls(
{
var it = original_it;
var decl_indexes_slot = first_decl_indexes_slot;
while (it.next()) |d| : (decl_indexes_slot += 1) {
const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
switch (decl_name_index) {
0 => {
const is_exported = @as(u1, @truncate(d.flags >> 1));
switch (is_exported) {
0 => continue, // comptime decl
1 => {
try self.analyzeUsingnamespaceDecl(
file,
scope,
parent_src,
decl_indexes,
priv_decl_indexes,
d,
call_ctx,
);
},
}
},
else => continue,
}
while (it.next()) |zir_index| : (decl_indexes_slot += 1) {
const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
if (extra.data.name != .@"usingnamespace") continue;
try self.analyzeUsingnamespaceDecl(
file,
scope,
try self.srcLocInfo(file, pl_node.src_node, parent_src),
decl_indexes,
priv_decl_indexes,
extra.data,
@intCast(extra.end),
call_ctx,
);
}
}
// Third loop to analyze all remaining decls
var it = original_it;
while (it.next()) |d| {
const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
switch (decl_name_index) {
0, 1 => continue, // skip over usingnamespace decls
2 => continue, // skip decltests
else => if (file.zir.string_bytes[decl_name_index] == 0) {
continue;
},
{
var it = original_it;
while (it.next()) |zir_index| {
const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
switch (extra.data.name) {
.@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
_ => if (extra.data.name.isNamedTest(file.zir)) continue,
}
try self.analyzeDecl(
file,
scope,
try self.srcLocInfo(file, pl_node.src_node, parent_src),
decl_indexes,
priv_decl_indexes,
zir_index,
extra.data,
@intCast(extra.end),
call_ctx,
);
}
try self.analyzeDecl(
file,
scope,
parent_src,
decl_indexes,
priv_decl_indexes,
d,
call_ctx,
);
}
// Fourth loop to analyze decltests
it = original_it;
while (it.next()) |d| {
const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
switch (decl_name_index) {
0, 1 => continue, // skip over usingnamespace decls
2 => {},
else => continue, // skip tests and normal decls
}
var it = original_it;
while (it.next()) |zir_index| {
const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
if (extra.data.name != .decltest) continue;
try self.analyzeDecltest(
file,
scope,
parent_src,
d,
try self.srcLocInfo(file, pl_node.src_node, parent_src),
extra.data,
@intCast(extra.end),
);
}
return it.extra_index;
}
fn walkInlineBody(
autodoc: *Autodoc,
file: *File,
scope: *Scope,
block_src: SrcLocInfo,
parent_src: SrcLocInfo,
body: []const Zir.Inst.Index,
need_type: bool,
call_ctx: ?*const CallContext,
) AutodocErrors!DocData.WalkResult {
const tags = file.zir.instructions.items(.tag);
const break_inst = switch (tags[@intFromEnum(body[body.len - 1])]) {
.condbr_inline => {
// Unresolvable.
const res: DocData.WalkResult = .{
.typeRef = .{ .type = @intFromEnum(Ref.type_type) },
.expr = .{ .comptimeExpr = autodoc.comptime_exprs.items.len },
};
const source = (try file.getTree(autodoc.zcu.gpa)).getNodeSource(block_src.src_node);
try autodoc.comptime_exprs.append(autodoc.arena, .{
.code = source,
});
return res;
},
.break_inline => body[body.len - 1],
else => unreachable,
};
const break_data = file.zir.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
return autodoc.walkRef(file, scope, parent_src, break_data.operand, need_type, call_ctx);
}
// Asserts the given decl is public
fn analyzeDecl(
self: *Autodoc,
file: *File,
scope: *Scope,
parent_src: SrcLocInfo,
decl_src: SrcLocInfo,
decl_indexes: *std.ArrayListUnmanaged(usize),
priv_decl_indexes: *std.ArrayListUnmanaged(usize),
d: Zir.DeclIterator.Item,
decl_inst: Zir.Inst.Index,
declaration: Zir.Inst.Declaration,
extra_index: u32,
call_ctx: ?*const CallContext,
) AutodocErrors!void {
const data = file.zir.instructions.items(.data);
const is_pub = @as(u1, @truncate(d.flags >> 0)) != 0;
// const is_exported = @truncate(u1, d.flags >> 1) != 0;
const has_align = @as(u1, @truncate(d.flags >> 2)) != 0;
const has_section_or_addrspace = @as(u1, @truncate(d.flags >> 3)) != 0;
const bodies = declaration.getBodies(extra_index, file.zir);
const name = file.zir.nullTerminatedString(declaration.name.toString(file.zir).?);
var extra_index = @intFromEnum(d.sub_index);
// const hash_u32s = file.zir.extra[extra_index..][0..4];
extra_index += 4;
// const line = file.zir.extra[extra_index];
extra_index += 1;
const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
break :inst inst;
};
_ = align_inst;
const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
break :inst inst;
};
_ = section_inst;
const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
extra_index += 1;
break :inst inst;
};
_ = addrspace_inst;
// This is known to work because decl values are always block_inlines
const value_pl_node = data[@intFromEnum(value_index)].pl_node;
const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
const name: []const u8 = switch (decl_name_index) {
.empty, .unnamed_test_decl, .decltest => unreachable,
_ => blk: {
if (decl_name_index == .empty) {
// test decl
unreachable;
}
break :blk file.zir.nullTerminatedString(decl_name_index);
},
};
const doc_comment: ?[]const u8 = if (doc_comment_index != .empty)
file.zir.nullTerminatedString(doc_comment_index)
const doc_comment: ?[]const u8 = if (declaration.flags.has_doc_comment)
file.zir.nullTerminatedString(@enumFromInt(file.zir.extra[extra_index]))
else
null;
@ -4261,16 +4212,22 @@ fn analyzeDecl(
break :idx idx;
};
const walk_result = try self.walkInstruction(
const walk_result = try self.walkInlineBody(
file,
scope,
decl_src,
value_index,
decl_src,
bodies.value_body,
true,
call_ctx,
);
const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const";
const tree = try file.getTree(self.zcu.gpa);
const kind_token = tree.nodes.items(.main_token)[decl_src.src_node];
const kind: []const u8 = switch (tree.tokens.items(.tag)[kind_token]) {
.keyword_var => "var",
else => "const",
};
const decls_slot_index = self.decls.items.len;
try self.decls.append(self.arena, .{
@ -4281,13 +4238,13 @@ fn analyzeDecl(
.parent_container = scope.enclosing_type,
});
if (is_pub) {
if (declaration.flags.is_pub) {
try decl_indexes.append(self.arena, decls_slot_index);
} else {
try priv_decl_indexes.append(self.arena, decls_slot_index);
}
const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, .none);
const decl_status_ptr = scope.resolveDeclName(declaration.name.toString(file.zir).?, file, .none);
std.debug.assert(decl_status_ptr.* == .Pending);
decl_status_ptr.* = .{ .Analyzed = decls_slot_index };
@ -4296,7 +4253,7 @@ fn analyzeDecl(
for (paths.items) |resume_info| {
try self.tryResolveRefPath(
resume_info.file,
value_index,
decl_inst,
resume_info.ref_path,
);
}
@ -4312,24 +4269,17 @@ fn analyzeUsingnamespaceDecl(
self: *Autodoc,
file: *File,
scope: *Scope,
parent_src: SrcLocInfo,
decl_src: SrcLocInfo,
decl_indexes: *std.ArrayListUnmanaged(usize),
priv_decl_indexes: *std.ArrayListUnmanaged(usize),
d: Zir.DeclIterator.Item,
declaration: Zir.Inst.Declaration,
extra_index: u32,
call_ctx: ?*const CallContext,
) AutodocErrors!void {
const data = file.zir.instructions.items(.data);
const bodies = declaration.getBodies(extra_index, file.zir);
const is_pub = @as(u1, @truncate(d.flags)) != 0;
const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 6]);
const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 7]);
// This is known to work because decl values are always block_inlines
const value_pl_node = data[@intFromEnum(value_index)].pl_node;
const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
const doc_comment: ?[]const u8 = if (doc_comment_index != .empty)
file.zir.nullTerminatedString(doc_comment_index)
const doc_comment: ?[]const u8 = if (declaration.flags.has_doc_comment)
file.zir.nullTerminatedString(@enumFromInt(file.zir.extra[extra_index]))
else
null;
@ -4346,11 +4296,12 @@ fn analyzeUsingnamespaceDecl(
break :idx idx;
};
const walk_result = try self.walkInstruction(
const walk_result = try self.walkInlineBody(
file,
scope,
decl_src,
value_index,
decl_src,
bodies.value_body,
true,
call_ctx,
);
@ -4365,7 +4316,7 @@ fn analyzeUsingnamespaceDecl(
.parent_container = scope.enclosing_type,
});
if (is_pub) {
if (declaration.flags.is_pub) {
try decl_indexes.append(self.arena, decl_slot_index);
} else {
try priv_decl_indexes.append(self.arena, decl_slot_index);
@ -4376,18 +4327,14 @@ fn analyzeDecltest(
self: *Autodoc,
file: *File,
scope: *Scope,
parent_src: SrcLocInfo,
d: Zir.DeclIterator.Item,
decl_src: SrcLocInfo,
declaration: Zir.Inst.Declaration,
extra_index: u32,
) AutodocErrors!void {
const data = file.zir.instructions.items(.data);
std.debug.assert(declaration.flags.has_doc_comment);
const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
const value_index = file.zir.extra[@intFromEnum(d.sub_index) + 6];
const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 7]);
const value_pl_node = data[value_index].pl_node;
const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
const test_source_code = try self.getBlockSource(file, parent_src, value_pl_node.src_node);
const test_source_code = (try file.getTree(self.zcu.gpa)).getNodeSource(decl_src.src_node);
const decl_name: ?[]const u8 = if (decl_name_index != .empty)
file.zir.nullTerminatedString(decl_name_index)
@ -5830,17 +5777,6 @@ fn walkRef(
}
}
fn getBlockInlineBreak(zir: Zir, inst: Zir.Inst.Index) ?Zir.Inst.Ref {
const tags = zir.instructions.items(.tag);
const data = zir.instructions.items(.data);
const pl_node = data[@intFromEnum(inst)].pl_node;
const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index);
const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
if (tags[break_index] == .condbr_inline) return null;
std.debug.assert(tags[break_index] == .break_inline);
return data[break_index].@"break".operand;
}
fn printWithContext(
file: *File,
inst: Zir.Inst.Index,

View File

@ -2802,6 +2802,7 @@ const Header = extern struct {
extra_len: u32,
limbs_len: u32,
string_bytes_len: u32,
tracked_insts_len: u32,
},
};
@ -2809,7 +2810,7 @@ const Header = extern struct {
/// saved, such as the target and most CLI flags. A cache hit will only occur
/// when subsequent compiler invocations use the same set of flags.
pub fn saveState(comp: *Compilation) !void {
var bufs_list: [6]std.os.iovec_const = undefined;
var bufs_list: [7]std.os.iovec_const = undefined;
var bufs_len: usize = 0;
const lf = comp.bin_file orelse return;
@ -2822,6 +2823,7 @@ pub fn saveState(comp: *Compilation) !void {
.extra_len = @intCast(ip.extra.items.len),
.limbs_len = @intCast(ip.limbs.items.len),
.string_bytes_len = @intCast(ip.string_bytes.items.len),
.tracked_insts_len = @intCast(ip.tracked_insts.count()),
},
};
addBuf(&bufs_list, &bufs_len, mem.asBytes(&header));
@ -2830,6 +2832,7 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.data)));
addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.tag)));
addBuf(&bufs_list, &bufs_len, ip.string_bytes.items);
addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys()));
// TODO: compilation errors
// TODO: files

View File

@ -54,6 +54,34 @@ string_table: std.HashMapUnmanaged(
std.hash_map.default_max_load_percentage,
) = .{},
/// An index into `tracked_insts` gives a reference to a single ZIR instruction which
/// persists across incremental updates.
tracked_insts: std.AutoArrayHashMapUnmanaged(TrackedInst, void) = .{},
pub const TrackedInst = extern struct {
path_digest: Cache.BinDigest,
inst: Zir.Inst.Index,
comptime {
// The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`.
assert(@sizeOf(@This()) == Cache.bin_digest_len + @sizeOf(Zir.Inst.Index));
}
pub const Index = enum(u32) {
_,
pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Zir.Inst.Index {
return ip.tracked_insts.keys()[@intFromEnum(i)].inst;
}
};
};
pub fn trackZir(ip: *InternPool, gpa: Allocator, file: *Module.File, inst: Zir.Inst.Index) Allocator.Error!TrackedInst.Index {
const key: TrackedInst = .{
.path_digest = file.path_digest,
.inst = inst,
};
const gop = try ip.tracked_insts.getOrPut(gpa, key);
return @enumFromInt(gop.index);
}
const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false);
const builtin = @import("builtin");
@ -62,11 +90,13 @@ const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const BigIntConst = std.math.big.int.Const;
const BigIntMutable = std.math.big.int.Mutable;
const Cache = std.Build.Cache;
const Limb = std.math.big.Limb;
const Hash = std.hash.Wyhash;
const InternPool = @This();
const Module = @import("Module.zig");
const Zcu = Module;
const Zir = @import("Zir.zig");
const KeyAdapter = struct {
@ -409,7 +439,7 @@ pub const Key = union(enum) {
/// `none` when the struct has no declarations.
namespace: OptionalNamespaceIndex,
/// Index of the struct_decl ZIR instruction.
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
layout: std.builtin.Type.ContainerLayout,
field_names: NullTerminatedString.Slice,
field_types: Index.Slice,
@ -653,7 +683,7 @@ pub const Key = union(enum) {
}
/// Asserts the struct is not packed.
pub fn setZirIndex(s: @This(), ip: *InternPool, new_zir_index: Zir.Inst.Index) void {
pub fn setZirIndex(s: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index) void {
assert(s.layout != .Packed);
const field_index = std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?;
ip.extra.items[s.extra_index + field_index] = @intFromEnum(new_zir_index);
@ -769,7 +799,7 @@ pub const Key = union(enum) {
flags: Tag.TypeUnion.Flags,
/// The enum that provides the list of field names and values.
enum_tag_ty: Index,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
/// The returned pointer expires with any addition to the `InternPool`.
pub fn flagsPtr(self: @This(), ip: *const InternPool) *Tag.TypeUnion.Flags {
@ -1056,7 +1086,7 @@ pub const Key = union(enum) {
/// the body. We store this rather than the body directly so that when ZIR
/// is regenerated on update(), we can map this to the new corresponding
/// ZIR instruction.
zir_body_inst: Zir.Inst.Index,
zir_body_inst: TrackedInst.Index,
/// Relative to owner Decl.
lbrace_line: u32,
/// Relative to owner Decl.
@ -1082,7 +1112,7 @@ pub const Key = union(enum) {
}
/// Returns a pointer that becomes invalid after any additions to the `InternPool`.
pub fn zirBodyInst(func: *const Func, ip: *const InternPool) *Zir.Inst.Index {
pub fn zirBodyInst(func: *const Func, ip: *const InternPool) *TrackedInst.Index {
return @ptrCast(&ip.extra.items[func.zir_body_inst_extra_index]);
}
@ -1860,7 +1890,7 @@ pub const UnionType = struct {
/// If this slice has length 0 it means all elements are `none`.
field_aligns: Alignment.Slice,
/// Index of the union_decl ZIR instruction.
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
/// Index into extra array of the `flags` field.
flags_index: u32,
/// Copied from `enum_tag_ty`.
@ -1954,10 +1984,10 @@ pub const UnionType = struct {
}
/// This does not mutate the field of UnionType.
pub fn setZirIndex(self: @This(), ip: *InternPool, new_zir_index: Zir.Inst.Index) void {
pub fn setZirIndex(self: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index) void {
const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
const zir_index_field_index = std.meta.fieldIndex(Tag.TypeUnion, "zir_index").?;
const ptr: *Zir.Inst.Index =
const ptr: *TrackedInst.Index =
@ptrCast(&ip.extra.items[self.flags_index - flags_field_index + zir_index_field_index]);
ptr.* = new_zir_index;
}
@ -2976,7 +3006,7 @@ pub const Tag = enum(u8) {
analysis: FuncAnalysis,
owner_decl: DeclIndex,
ty: Index,
zir_body_inst: Zir.Inst.Index,
zir_body_inst: TrackedInst.Index,
lbrace_line: u32,
rbrace_line: u32,
lbrace_column: u32,
@ -3050,7 +3080,7 @@ pub const Tag = enum(u8) {
namespace: NamespaceIndex,
/// The enum that provides the list of field names and values.
tag_ty: Index,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
pub const Flags = packed struct(u32) {
runtime_tag: UnionType.RuntimeTag,
@ -3072,7 +3102,7 @@ pub const Tag = enum(u8) {
/// 2. init: Index for each fields_len // if tag is type_struct_packed_inits
pub const TypeStructPacked = struct {
decl: DeclIndex,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
namespace: OptionalNamespaceIndex,
backing_int_ty: Index,
@ -3119,7 +3149,7 @@ pub const Tag = enum(u8) {
/// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved
pub const TypeStruct = struct {
decl: DeclIndex,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
flags: Flags,
size: u32,
@ -3708,6 +3738,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
ip.string_table.deinit(gpa);
ip.tracked_insts.deinit(gpa);
ip.* = undefined;
}
@ -5358,7 +5390,7 @@ pub const UnionTypeInit = struct {
flags: Tag.TypeUnion.Flags,
decl: DeclIndex,
namespace: NamespaceIndex,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
enum_tag_ty: Index,
/// May have length 0 which leaves the values unset until later.
@ -5430,7 +5462,7 @@ pub const StructTypeInit = struct {
decl: DeclIndex,
namespace: OptionalNamespaceIndex,
layout: std.builtin.Type.ContainerLayout,
zir_index: Zir.Inst.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
known_non_opv: bool,
requires_comptime: RequiresComptime,
@ -5704,7 +5736,7 @@ pub fn getExternFunc(ip: *InternPool, gpa: Allocator, key: Key.ExternFunc) Alloc
pub const GetFuncDeclKey = struct {
owner_decl: DeclIndex,
ty: Index,
zir_body_inst: Zir.Inst.Index,
zir_body_inst: TrackedInst.Index,
lbrace_line: u32,
rbrace_line: u32,
lbrace_column: u32,
@ -5773,7 +5805,7 @@ pub const GetFuncDeclIesKey = struct {
is_var_args: bool,
is_generic: bool,
is_noinline: bool,
zir_body_inst: Zir.Inst.Index,
zir_body_inst: TrackedInst.Index,
lbrace_line: u32,
rbrace_line: u32,
lbrace_column: u32,
@ -6186,8 +6218,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,
});
@ -6537,7 +6567,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
NullTerminatedString,
OptionalNullTerminatedString,
Tag.TypePointer.VectorIndex,
Zir.Inst.Index,
TrackedInst.Index,
=> @intFromEnum(@field(extra, field.name)),
u32,
@ -6613,7 +6643,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
NullTerminatedString,
OptionalNullTerminatedString,
Tag.TypePointer.VectorIndex,
Zir.Inst.Index,
TrackedInst.Index,
=> @enumFromInt(int32),
u32,
@ -8319,7 +8349,7 @@ pub fn funcHasInferredErrorSet(ip: *const InternPool, i: Index) bool {
return funcAnalysis(ip, i).inferred_error_set;
}
pub fn funcZirBodyInst(ip: *const InternPool, i: Index) Zir.Inst.Index {
pub fn funcZirBodyInst(ip: *const InternPool, i: Index) TrackedInst.Index {
assert(i != .none);
const item = ip.items.get(@intFromEnum(i));
const zir_body_inst_field_index = std.meta.fieldIndex(Tag.FuncDecl, "zir_body_inst").?;

View File

@ -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,81 +465,19 @@ 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,
};
/// This name is relative to the containing namespace of the decl.
/// The memory is owned by the containing File ZIR.
pub fn getName(decl: Decl, mod: *Module) ?[:0]const u8 {
const zir = decl.getFileScope(mod).zir;
return decl.getNameZir(zir);
/// 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;
}
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);
}
pub fn contentsHash(decl: Decl, mod: *Module) std.zig.SrcHash {
const zir = decl.getFileScope(mod).zir;
return decl.contentsHashZir(zir);
}
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]);
}
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 {
@ -902,6 +834,9 @@ pub const File = struct {
multi_pkg: bool = false,
/// List of references to this file, used for multi-package errors.
references: std.ArrayListUnmanaged(Reference) = .{},
/// The hash of the path to this file, used to store `InternPool.TrackedInst`.
/// undefined until `zir_loaded == true`.
path_digest: Cache.BinDigest = undefined,
/// Used by change detection algorithm, after astgen, contains the
/// set of decls that existed in the previous ZIR but not in the new one.
@ -2662,7 +2597,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
const stat = try source_file.stat();
const want_local_cache = file.mod == mod.main_mod;
const digest = hash: {
const bin_digest = hash: {
var path_hash: Cache.HashHelper = .{};
path_hash.addBytes(build_options.version);
path_hash.add(builtin.zig_backend);
@ -2671,7 +2606,19 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
path_hash.addBytes(file.mod.root.sub_path);
}
path_hash.addBytes(file.sub_file_path);
break :hash path_hash.final();
var bin: Cache.BinDigest = undefined;
path_hash.hasher.final(&bin);
break :hash bin;
};
file.path_digest = bin_digest;
const hex_digest = hex: {
var hex: Cache.HexDigest = undefined;
_ = std.fmt.bufPrint(
&hex,
"{s}",
.{std.fmt.fmtSliceHexLower(&bin_digest)},
) catch unreachable;
break :hex hex;
};
const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;
const zir_dir = cache_directory.handle;
@ -2681,7 +2628,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
.never_loaded, .retryable_failure => lock: {
// First, load the cached ZIR code, if any.
log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{
file.sub_file_path, want_local_cache, &digest,
file.sub_file_path, want_local_cache, &hex_digest,
});
break :lock .shared;
@ -2708,7 +2655,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
// version. Likewise if we're working on AstGen and another process asks for
// the cached file, they'll get it.
const cache_file = while (true) {
break zir_dir.createFile(&digest, .{
break zir_dir.createFile(&hex_digest, .{
.read = true,
.truncate = false,
.lock = lock,
@ -2894,7 +2841,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
};
cache_file.writevAll(&iovecs) catch |err| {
log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
file.mod.root, file.sub_file_path, cache_directory, &digest, @errorName(err),
file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
});
};
@ -3003,93 +2950,22 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File)
return zir;
}
/// Patch ups:
/// * Struct.zir_index
/// * Decl.zir_index
/// * Fn.zir_body_inst
/// * Decl.zir_decl_index
fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
const gpa = mod.gpa;
const new_zir = file.zir;
fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void {
const gpa = zcu.gpa;
// 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.
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, file.zir, &inst_map);
// Walk the Decl graph, updating ZIR indexes, strings, and populating
// the deleted and outdated lists.
var decl_stack: ArrayListUnmanaged(Decl.Index) = .{};
defer decl_stack.deinit(gpa);
try decl_stack.append(gpa, root_decl);
file.deleted_decls.clearRetainingCapacity();
file.outdated_decls.clearRetainingCapacity();
// The root decl is always outdated; otherwise we would not have had
// to re-generate ZIR for the File.
try file.outdated_decls.append(gpa, root_decl);
const ip = &mod.intern_pool;
while (decl_stack.popOrNull()) |decl_index| {
const decl = mod.declPtr(decl_index);
// Anonymous decls and the root decl have this set to 0. We still need
// to walk them but we do not need to modify this value.
// 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 {
try file.deleted_decls.append(gpa, decl_index);
continue;
};
const old_hash = decl.contentsHashZir(old_zir);
decl.zir_decl_index = new_zir_decl_index.toOptional();
const new_hash = decl.contentsHashZir(new_zir);
if (!std.zig.srcHashEql(old_hash, new_hash)) {
try file.outdated_decls.append(gpa, decl_index);
}
}
if (!decl.owns_tv) continue;
if (decl.getOwnedStruct(mod)) |struct_type| {
struct_type.setZirIndex(ip, inst_map.get(struct_type.zir_index) orelse {
try file.deleted_decls.append(gpa, decl_index);
continue;
});
}
if (decl.getOwnedUnion(mod)) |union_type| {
union_type.setZirIndex(ip, inst_map.get(union_type.zir_index) orelse {
try file.deleted_decls.append(gpa, decl_index);
continue;
});
}
if (decl.getOwnedFunction(mod)) |func| {
func.zirBodyInst(ip).* = inst_map.get(func.zir_body_inst) orelse {
try file.deleted_decls.append(gpa, decl_index);
continue;
};
}
if (decl.getOwnedInnerNamespace(mod)) |namespace| {
for (namespace.decls.keys()) |sub_decl| {
try decl_stack.append(gpa, sub_decl);
}
}
// TODO: this should be done after all AstGen workers complete, to avoid
// iterating over this full set for every updated file.
for (zcu.intern_pool.tracked_insts.keys()) |*ti| {
if (!std.mem.eql(u8, &ti.path_digest, &file.path_digest)) continue;
ti.inst = inst_map.get(ti.inst) orelse {
// TODO: invalidate this `TrackedInst` via the dependency mechanism
continue;
};
}
}
@ -3098,9 +2974,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 +2984,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 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);
var unnamed_test_idx: u32 = 0;
var comptime_decl_idx: u32 = 0;
var usingnamespace_decl_idx: u32 = 0;
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],
});
var new_decl_it = new_zir.declIterator(match_item.new_inst);
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 +3399,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;
@ -3502,7 +3442,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
const struct_ty = sema.getStructType(
new_decl_index,
new_namespace_index,
.main_struct_inst,
try mod.intern_pool.trackZir(gpa, file, .main_struct_inst),
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
};
@ -3561,7 +3501,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;
@ -3639,11 +3578,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;
@ -3760,13 +3697,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",
});
@ -3786,15 +3723,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;
@ -4133,52 +4070,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,
@ -4186,119 +4103,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;
// 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 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;
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,
};
},
.unnamed_test_decl => name: {
.@"usingnamespace" => info: {
const i = iter.usingnamespace_index;
iter.usingnamespace_index += 1;
break :info .{
try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}),
.@"usingnamespace",
false,
};
},
.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) {
@ -4307,46 +4217,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 });
@ -4514,7 +4420,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
};
defer inner_block.instructions.deinit(gpa);
const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).*);
const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).resolve(ip));
// Here we are performing "runtime semantic analysis" for a function body, which means
// we must map the parameter ZIR instructions to `arg` AIR instructions.
@ -4730,8 +4636,6 @@ pub fn allocateNewDecl(
.generation = 0,
.is_pub = false,
.is_exported = false,
.has_linksection_or_addrspace = false,
.has_align = false,
.alive = false,
.kind = .anon,
});
@ -6169,7 +6073,7 @@ pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]
const tags = file.zir.instructions.items(.tag);
const data = file.zir.instructions.items(.data);
const param_body = file.zir.getParamBody(func.zir_body_inst);
const param_body = file.zir.getParamBody(func.zir_body_inst.resolve(&mod.intern_pool));
const param = param_body[index];
return switch (tags[@intFromEnum(param)]) {

View File

@ -1224,6 +1224,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) {
@ -2704,11 +2708,12 @@ pub fn getStructType(
sema: *Sema,
decl: InternPool.DeclIndex,
namespace: InternPool.NamespaceIndex,
zir_index: Zir.Inst.Index,
tracked_inst: InternPool.TrackedInst.Index,
) !InternPool.Index {
const mod = sema.mod;
const gpa = sema.gpa;
const ip = &mod.intern_pool;
const zir_index = tracked_inst.resolve(ip);
const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended;
assert(extended.opcode == .struct_decl);
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
@ -2736,12 +2741,14 @@ 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,
.namespace = namespace.toOptional(),
.zir_index = zir_index,
.zir_index = tracked_inst,
.layout = small.layout,
.known_non_opv = small.known_non_opv,
.is_tuple = small.is_tuple,
@ -2791,7 +2798,8 @@ fn zirStructDecl(
errdefer mod.destroyNamespace(new_namespace_index);
const struct_ty = ty: {
const ty = try sema.getStructType(new_decl_index, new_namespace_index, inst);
const tracked_inst = try ip.trackZir(mod.gpa, block.getFileScope(mod), inst);
const ty = try sema.getStructType(new_decl_index, new_namespace_index, tracked_inst);
if (sema.builtin_type_target_index != .none) {
ip.resolveBuiltinType(sema.builtin_type_target_index, ty);
break :ty sema.builtin_type_target_index;
@ -2850,7 +2858,7 @@ fn createAnonymousDeclTypeNamed(
return new_decl_index;
},
.func => {
const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index));
const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip));
const zir_tags = sema.code.instructions.items(.tag);
var buf = std.ArrayList(u8).init(gpa);
@ -2973,7 +2981,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;
@ -3244,7 +3254,7 @@ fn zirUnionDecl(
},
.decl = new_decl_index,
.namespace = new_namespace_index,
.zir_index = inst,
.zir_index = try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst),
.fields_len = fields_len,
.enum_tag_ty = .none,
.field_types = &.{},
@ -3263,7 +3273,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);
@ -3326,7 +3337,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);
@ -7436,7 +7448,7 @@ fn analyzeCall(
// the AIR instructions of the callsite. The callee could be a generic function
// which means its parameter type expressions must be resolved in order and used
// to successively coerce the arguments.
const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst);
const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip));
try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
var arg_i: u32 = 0;
@ -7484,7 +7496,7 @@ fn analyzeCall(
// each of the parameters, resolving the return type and providing it to the child
// `Sema` so that it can be used for the `ret_ptr` instruction.
const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip))
else
try sema.resolveInst(fn_info.ret_ty_ref);
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
@ -7875,7 +7887,7 @@ fn instantiateGenericCall(
const namespace_index = fn_owner_decl.src_namespace;
const namespace = mod.namespacePtr(namespace_index);
const fn_zir = namespace.file_scope.zir;
const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst);
const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip));
const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
@memset(comptime_args, .none);
@ -9457,7 +9469,7 @@ fn funcCommon(
.is_generic = final_is_generic,
.is_noinline = is_noinline,
.zir_body_inst = func_inst,
.zir_body_inst = try ip.trackZir(gpa, block.getFileScope(mod), func_inst),
.lbrace_line = src_locs.lbrace_line,
.rbrace_line = src_locs.rbrace_line,
.lbrace_column = @as(u16, @truncate(src_locs.columns)),
@ -9535,7 +9547,7 @@ fn funcCommon(
.ty = func_ty,
.cc = cc,
.is_noinline = is_noinline,
.zir_body_inst = func_inst,
.zir_body_inst = try ip.trackZir(gpa, block.getFileScope(mod), func_inst),
.lbrace_line = src_locs.lbrace_line,
.rbrace_line = src_locs.rbrace_line,
.lbrace_column = @as(u16, @truncate(src_locs.columns)),
@ -21552,7 +21564,7 @@ fn zirReify(
.namespace = new_namespace_index,
.enum_tag_ty = enum_tag_ty,
.fields_len = fields_len,
.zir_index = inst,
.zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), // TODO: should reified types be handled differently?
.flags = .{
.layout = layout,
.status = .have_field_types,
@ -21720,7 +21732,7 @@ fn reifyStruct(
const ty = try ip.getStructType(gpa, .{
.decl = new_decl_index,
.namespace = .none,
.zir_index = inst,
.zir_index = try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst), // TODO: should reified types be handled differently?
.layout = layout,
.known_non_opv = false,
.fields_len = fields_len,
@ -35592,7 +35604,8 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
break :blk accumulator;
};
const extended = zir.instructions.items(.data)[@intFromEnum(struct_type.zir_index)].extended;
const zir_index = struct_type.zir_index.resolve(ip);
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
assert(extended.opcode == .struct_decl);
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
@ -35612,7 +35625,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
} else {
const body = zir.bodySlice(extra_index, backing_int_body_len);
const ty_ref = try sema.resolveBody(&block, body, struct_type.zir_index);
const ty_ref = try sema.resolveBody(&block, body, zir_index);
break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
}
};
@ -36340,9 +36353,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 };
}
@ -36358,7 +36369,7 @@ fn semaStructFields(
const decl = mod.declPtr(decl_index);
const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
const zir = mod.namespacePtr(namespace_index).file_scope.zir;
const zir_index = struct_type.zir_index;
const zir_index = struct_type.zir_index.resolve(ip);
const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
@ -36629,7 +36640,7 @@ fn semaStructFieldInits(
const decl = mod.declPtr(decl_index);
const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
const zir = mod.namespacePtr(namespace_index).file_scope.zir;
const zir_index = struct_type.zir_index;
const zir_index = struct_type.zir_index.resolve(ip);
const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
@ -36778,7 +36789,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
const ip = &mod.intern_pool;
const decl_index = union_type.decl;
const zir = mod.namespacePtr(union_type.namespace).file_scope.zir;
const extended = zir.instructions.items(.data)[@intFromEnum(union_type.zir_index)].extended;
const zir_index = union_type.zir_index.resolve(ip);
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
assert(extended.opcode == .union_decl);
const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
var extra_index: usize = extended.operand;
@ -36811,9 +36823,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;
@ -37810,10 +37820,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;

View File

@ -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),
};
}

View File

@ -6855,10 +6855,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();
@ -6867,16 +6864,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.*),
});
}
}

View File

@ -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");