stage2: implement caching for ZIR code

Notably this exposed an issue with the language having to do with the
secret safety tag on untagged unions. How can we have our cake and eat
it too? Not solved in this commit. I will file a language proposal to
tackle this issue soon.

Fixes a compile error in `std.fs.File.readvAll`.
This commit is contained in:
Andrew Kelley 2021-04-25 00:02:58 -07:00
parent ff2ec0dc5a
commit 015cd79f89
6 changed files with 587 additions and 22 deletions

View File

@ -1,15 +1,10 @@
* nested function decl: how to refer to params?
* look for cached zir code
* save zir code to cache
* keep track of file dependencies/dependants * keep track of file dependencies/dependants
* unload files from memory when a dependency is dropped * unload files from memory when a dependency is dropped
* implement zir error notes
* implement the new AstGen compile errors * implement the new AstGen compile errors
* get rid of failed_root_src_file * get rid of failed_root_src_file
* get rid of Scope.DeclRef * get rid of Scope.DeclRef
* get rid of optional_type_from_ptr_elem
* handle decl collision with usingnamespace * handle decl collision with usingnamespace
* the decl doing the looking up needs to create a decl dependency * the decl doing the looking up needs to create a decl dependency
on each usingnamespace decl on each usingnamespace decl
@ -38,6 +33,9 @@
AstGen can report more than one compile error. AstGen can report more than one compile error.
* AstGen: add result location pointers to function calls * AstGen: add result location pointers to function calls
* nested function decl: how to refer to params?
* detect when to put cached ZIR into the local cache instead of the global one
const container_name_hash: Scope.NameHash = if (found_pkg) |pkg| const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|
pkg.namespace_hash pkg.namespace_hash

View File

@ -482,7 +482,7 @@ pub const File = struct {
/// order to handle partial reads from the underlying OS layer. /// order to handle partial reads from the underlying OS layer.
/// See https://github.com/ziglang/zig/issues/7699 /// See https://github.com/ziglang/zig/issues/7699
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
if (iovecs.len == 0) return; if (iovecs.len == 0) return 0;
var i: usize = 0; var i: usize = 0;
var off: usize = 0; var off: usize = 0;

View File

@ -434,10 +434,10 @@ pub const AllErrors = struct {
arena: *Allocator, arena: *Allocator,
errors: *std.ArrayList(Message), errors: *std.ArrayList(Message),
file: *Module.Scope.File, file: *Module.Scope.File,
source: []const u8,
) !void { ) !void {
assert(file.zir_loaded); assert(file.zir_loaded);
assert(file.tree_loaded); assert(file.tree_loaded);
assert(file.source_loaded);
const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)];
assert(payload_index != 0); assert(payload_index != 0);
@ -466,7 +466,7 @@ pub const AllErrors = struct {
} }
break :blk token_starts[note_item.data.token] + note_item.data.byte_offset; break :blk token_starts[note_item.data.token] + note_item.data.byte_offset;
}; };
const loc = std.zig.findLineColumn(source, byte_offset); const loc = std.zig.findLineColumn(file.source, byte_offset);
note.* = .{ note.* = .{
.src = .{ .src = .{
@ -492,7 +492,7 @@ pub const AllErrors = struct {
} }
break :blk token_starts[item.data.token] + item.data.byte_offset; break :blk token_starts[item.data.token] + item.data.byte_offset;
}; };
const loc = std.zig.findLineColumn(source, byte_offset); const loc = std.zig.findLineColumn(file.source, byte_offset);
try errors.append(.{ try errors.append(.{
.src = .{ .src = .{
@ -1709,9 +1709,11 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
if (entry.value) |msg| { if (entry.value) |msg| {
try AllErrors.add(module, &arena, &errors, msg.*); try AllErrors.add(module, &arena, &errors, msg.*);
} else { } else {
// Must be ZIR errors. // Must be ZIR errors. In order for ZIR errors to exist, the parsing
const source = try entry.key.getSource(module.gpa); // must have completed successfully.
try AllErrors.addZir(&arena.allocator, &errors, entry.key, source); const tree = try entry.key.getTree(module.gpa);
assert(tree.errors.len == 0);
try AllErrors.addZir(&arena.allocator, &errors, entry.key);
} }
} }
for (module.failed_decls.items()) |entry| { for (module.failed_decls.items()) |entry| {

View File

@ -15,6 +15,7 @@ const ast = std.zig.ast;
const Module = @This(); const Module = @This();
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
const Cache = @import("Cache.zig");
const Value = @import("value.zig").Value; const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type; const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig"); const TypedValue = @import("TypedValue.zig");
@ -771,6 +772,15 @@ pub const Scope = struct {
return source; return source;
} }
pub fn getTree(file: *File, gpa: *Allocator) !*const ast.Tree {
if (file.tree_loaded) return &file.tree;
const source = try file.getSource(gpa);
file.tree = try std.zig.parse(gpa, source);
file.tree_loaded = true;
return &file.tree;
}
pub fn destroy(file: *File, gpa: *Allocator) void { pub fn destroy(file: *File, gpa: *Allocator) void {
file.deinit(gpa); file.deinit(gpa);
gpa.destroy(file); gpa.destroy(file);
@ -2676,6 +2686,20 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
gpa.free(export_list); gpa.free(export_list);
} }
const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
// TODO This is taking advantage of matching stage1 debug union layout.
// We need a better language feature for initializing a union with
// a runtime known tag.
const Stage1DataLayout = extern struct {
safety_tag: u8,
data: [8]u8 align(8),
};
comptime {
if (data_has_safety_tag) {
assert(@sizeOf(Stage1DataLayout) == @sizeOf(Zir.Inst.Data));
}
}
pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node) !void { pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -2684,15 +2708,166 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
const gpa = mod.gpa; const gpa = mod.gpa;
// In any case we need to examine the stat of the file to determine the course of action. // In any case we need to examine the stat of the file to determine the course of action.
var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{}); var source_file = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{});
defer f.close(); defer source_file.close();
const stat = try f.stat(); const stat = try source_file.stat();
const want_local_cache = file.pkg == mod.root_pkg;
const digest = hash: {
var path_hash: Cache.HashHelper = .{};
if (!want_local_cache) {
path_hash.addOptionalBytes(file.pkg.root_src_directory.path);
}
path_hash.addBytes(file.sub_file_path);
break :hash path_hash.final();
};
const cache_directory = if (want_local_cache)
comp.local_cache_directory
else
comp.global_cache_directory;
var cache_file: ?std.fs.File = null;
defer if (cache_file) |f| f.close();
// TODO do this before spawning astgen workers
var zir_dir = try cache_directory.handle.makeOpenPath("z", .{});
defer zir_dir.close();
// Determine whether we need to reload the file from disk and redo parsing and AstGen. // Determine whether we need to reload the file from disk and redo parsing and AstGen.
switch (file.status) { switch (file.status) {
.never_loaded, .retryable_failure => { .never_loaded, .retryable_failure => cached: {
log.debug("first-time AstGen: {s}", .{file.sub_file_path}); // 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,
});
// We ask for a lock in order to coordinate with other zig processes.
// If another process is already working on this file, we will get the cached
// version. Likewise if we're working on AstGen and another process asks for
// the cached file, they'll get it.
cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) {
error.PathAlreadyExists => unreachable, // opening for reading
error.NoSpaceLeft => unreachable, // opening for reading
error.NotDir => unreachable, // no dir components
error.InvalidUtf8 => unreachable, // it's a hex encoded name
error.BadPathName => unreachable, // it's a hex encoded name
error.NameTooLong => unreachable, // it's a fixed size name
error.PipeBusy => unreachable, // it's not a pipe
error.WouldBlock => unreachable, // not asking for non-blocking I/O
error.SymLinkLoop,
error.FileNotFound,
error.Unexpected,
=> break :cached,
else => |e| return e, // Retryable errors are handled at callsite.
};
// First we read the header to determine the lengths of arrays.
const header = cache_file.?.reader().readStruct(Zir.Header) catch |err| switch (err) {
// This can happen if Zig bails out of this function between creating
// the cached file and writing it.
error.EndOfStream => break :cached,
else => |e| return e,
};
const unchanged_metadata =
stat.size == header.stat_size and
stat.mtime == header.stat_mtime and
stat.inode == header.stat_inode;
if (!unchanged_metadata) {
log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
break :cached;
}
log.debug("AstGen cache hit: {s}", .{file.sub_file_path});
var instructions: std.MultiArrayList(Zir.Inst) = .{};
defer instructions.deinit(gpa);
try instructions.resize(gpa, header.instructions_len);
var zir: Zir = .{
.instructions = instructions.toOwnedSlice(),
.string_bytes = &.{},
.extra = &.{},
};
var keep_zir = false;
defer if (!keep_zir) zir.deinit(gpa);
zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
zir.extra = try gpa.alloc(u32, header.extra_len);
const safety_buffer = if (data_has_safety_tag)
try gpa.alloc([8]u8, header.instructions_len)
else
undefined;
defer if (data_has_safety_tag) gpa.free(safety_buffer);
const data_ptr = if (data_has_safety_tag)
@ptrCast([*]u8, safety_buffer.ptr)
else
@ptrCast([*]u8, zir.instructions.items(.data).ptr);
var iovecs = [_]std.os.iovec{
.{
.iov_base = @ptrCast([*]u8, zir.instructions.items(.tag).ptr),
.iov_len = header.instructions_len,
},
.{
.iov_base = data_ptr,
.iov_len = header.instructions_len * 8,
},
.{
.iov_base = zir.string_bytes.ptr,
.iov_len = header.string_bytes_len,
},
.{
.iov_base = @ptrCast([*]u8, zir.extra.ptr),
.iov_len = header.extra_len * 4,
},
};
const amt_read = try cache_file.?.readvAll(&iovecs);
const amt_expected = zir.instructions.len * 9 +
zir.string_bytes.len +
zir.extra.len * 4;
if (amt_read != amt_expected) {
log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
zir.deinit(gpa);
break :cached;
}
if (data_has_safety_tag) {
const tags = zir.instructions.items(.tag);
for (zir.instructions.items(.data)) |*data, i| {
const union_tag = Zir.Inst.Tag.data_tags[@enumToInt(tags[i])];
const as_struct = @ptrCast(*Stage1DataLayout, data);
as_struct.* = .{
.safety_tag = @enumToInt(union_tag),
.data = safety_buffer[i],
};
}
}
keep_zir = true;
file.zir = zir;
file.zir_loaded = true;
file.stat_size = header.stat_size;
file.stat_inode = header.stat_inode;
file.stat_mtime = header.stat_mtime;
file.status = .success;
log.debug("AstGen cached success: {s}", .{file.sub_file_path});
// TODO don't report compile errors until Sema @importFile
if (file.zir.hasCompileErrors()) {
{
const lock = comp.mutex.acquire();
defer lock.release();
try mod.failed_files.putNoClobber(gpa, file, null);
}
file.status = .astgen_failure;
return error.AnalysisFail;
}
return;
}, },
.parse_failure, .astgen_failure, .success => { .parse_failure, .astgen_failure, .success => {
const unchanged_metadata = const unchanged_metadata =
@ -2708,6 +2883,29 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
log.debug("metadata changed: {s}", .{file.sub_file_path}); log.debug("metadata changed: {s}", .{file.sub_file_path});
}, },
} }
if (cache_file) |f| {
f.close();
cache_file = null;
}
cache_file = zir_dir.createFile(&digest, .{ .lock = .Exclusive }) catch |err| switch (err) {
error.NotDir => unreachable, // no dir components
error.InvalidUtf8 => unreachable, // it's a hex encoded name
error.BadPathName => unreachable, // it's a hex encoded name
error.NameTooLong => unreachable, // it's a fixed size name
error.PipeBusy => unreachable, // it's not a pipe
error.WouldBlock => unreachable, // not asking for non-blocking I/O
error.FileNotFound => unreachable, // no dir components
else => |e| {
const pkg_path = file.pkg.root_src_directory.path orelse ".";
const cache_path = cache_directory.path orelse ".";
log.warn("unable to save cached ZIR code for {s}/{s} to {s}/z/{s}: {s}", .{
pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e),
});
return;
},
};
// Clear compile error for this file. // Clear compile error for this file.
switch (file.status) { switch (file.status) {
.success, .retryable_failure => {}, .success, .retryable_failure => {},
@ -2726,7 +2924,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
const source = try gpa.allocSentinel(u8, stat.size, 0); const source = try gpa.allocSentinel(u8, stat.size, 0);
defer if (!file.source_loaded) gpa.free(source); defer if (!file.source_loaded) gpa.free(source);
const amt = try f.readAll(source); const amt = try source_file.readAll(source);
if (amt != stat.size) if (amt != stat.size)
return error.UnexpectedEndOfFile; return error.UnexpectedEndOfFile;
@ -2770,7 +2968,67 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
file.zir = try AstGen.generate(gpa, file); file.zir = try AstGen.generate(gpa, file);
file.zir_loaded = true; file.zir_loaded = true;
file.status = .success;
log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
const safety_buffer = if (data_has_safety_tag)
try gpa.alloc([8]u8, file.zir.instructions.len)
else
undefined;
defer if (data_has_safety_tag) gpa.free(safety_buffer);
const data_ptr = if (data_has_safety_tag)
@ptrCast([*]const u8, safety_buffer.ptr)
else
@ptrCast([*]const u8, file.zir.instructions.items(.data).ptr);
if (data_has_safety_tag) {
// The `Data` union has a safety tag but in the file format we store it without.
const tags = file.zir.instructions.items(.tag);
for (file.zir.instructions.items(.data)) |*data, i| {
const as_struct = @ptrCast(*const Stage1DataLayout, data);
safety_buffer[i] = as_struct.data;
}
}
const header: Zir.Header = .{
.instructions_len = @intCast(u32, file.zir.instructions.len),
.string_bytes_len = @intCast(u32, file.zir.string_bytes.len),
.extra_len = @intCast(u32, file.zir.extra.len),
.stat_size = stat.size,
.stat_inode = stat.inode,
.stat_mtime = stat.mtime,
};
var iovecs = [_]std.os.iovec_const{
.{
.iov_base = @ptrCast([*]const u8, &header),
.iov_len = @sizeOf(Zir.Header),
},
.{
.iov_base = @ptrCast([*]const u8, file.zir.instructions.items(.tag).ptr),
.iov_len = file.zir.instructions.len,
},
.{
.iov_base = data_ptr,
.iov_len = file.zir.instructions.len * 8,
},
.{
.iov_base = file.zir.string_bytes.ptr,
.iov_len = file.zir.string_bytes.len,
},
.{
.iov_base = @ptrCast([*]const u8, file.zir.extra.ptr),
.iov_len = file.zir.extra.len * 4,
},
};
cache_file.?.writevAll(&iovecs) catch |err| {
const pkg_path = file.pkg.root_src_directory.path orelse ".";
const cache_path = cache_directory.path orelse ".";
log.warn("unable to write cached ZIR code for {s}/{s} to {s}/z/{s}: {s}", .{
pkg_path, file.sub_file_path, cache_path, &digest, @errorName(err),
});
};
// TODO don't report compile errors until Sema @importFile
if (file.zir.hasCompileErrors()) { if (file.zir.hasCompileErrors()) {
{ {
const lock = comp.mutex.acquire(); const lock = comp.mutex.acquire();
@ -2780,9 +3038,6 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
file.status = .astgen_failure; file.status = .astgen_failure;
return error.AnalysisFail; return error.AnalysisFail;
} }
log.debug("AstGen success: {s}", .{file.sub_file_path});
file.status = .success;
} }
pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {

View File

@ -37,6 +37,17 @@ string_bytes: []u8,
/// The first few indexes are reserved. See `ExtraIndex` for the values. /// The first few indexes are reserved. See `ExtraIndex` for the values.
extra: []u32, extra: []u32,
/// The data stored at byte offset 0 when ZIR is stored in a file.
pub const Header = extern struct {
instructions_len: u32,
string_bytes_len: u32,
extra_len: u32,
stat_size: u64,
stat_inode: std.fs.File.INode,
stat_mtime: i128,
};
pub const ExtraIndex = enum(u32) { pub const ExtraIndex = enum(u32) {
/// Ref. The main struct decl for this file. /// Ref. The main struct decl for this file.
main_struct, main_struct,
@ -139,6 +150,7 @@ pub const Inst = struct {
data: Data, data: Data,
/// These names are used directly as the instruction names in the text format. /// These names are used directly as the instruction names in the text format.
/// See `data_field_map` for a list of which `Data` fields are used by each `Tag`.
pub const Tag = enum(u8) { pub const Tag = enum(u8) {
/// Arithmetic addition, asserts no integer overflow. /// Arithmetic addition, asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`. /// Uses the `pl_node` union field. Payload is `Bin`.
@ -932,6 +944,7 @@ pub const Inst = struct {
/// Uses the `un_node` field. The AST node is the var decl. /// Uses the `un_node` field. The AST node is the var decl.
resolve_inferred_alloc, resolve_inferred_alloc,
/// Implements `resume` syntax. Uses `un_node` field.
@"resume", @"resume",
@"await", @"await",
await_nosuspend, await_nosuspend,
@ -1202,6 +1215,276 @@ pub const Inst = struct {
=> true, => true,
}; };
} }
/// Used by debug safety-checking code.
pub const data_tags = list: {
@setEvalBranchQuota(2000);
break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
.add = .pl_node,
.addwrap = .pl_node,
.array_cat = .pl_node,
.array_mul = .pl_node,
.array_type = .bin,
.array_type_sentinel = .array_type_sentinel,
.vector_type = .pl_node,
.elem_type = .un_node,
.indexable_ptr_len = .un_node,
.anyframe_type = .un_node,
.as = .bin,
.as_node = .pl_node,
.bit_and = .pl_node,
.bitcast = .pl_node,
.bitcast_result_ptr = .pl_node,
.bit_not = .un_node,
.bit_or = .pl_node,
.block = .pl_node,
.block_inline = .pl_node,
.block_inline_var = .pl_node,
.suspend_block = .pl_node,
.bool_and = .pl_node,
.bool_not = .un_node,
.bool_or = .pl_node,
.bool_br_and = .bool_br,
.bool_br_or = .bool_br,
.@"break" = .@"break",
.break_inline = .@"break",
.breakpoint = .node,
.call = .pl_node,
.call_chkused = .pl_node,
.call_compile_time = .pl_node,
.call_nosuspend = .pl_node,
.call_async = .pl_node,
.cmp_lt = .pl_node,
.cmp_lte = .pl_node,
.cmp_eq = .pl_node,
.cmp_gte = .pl_node,
.cmp_gt = .pl_node,
.cmp_neq = .pl_node,
.coerce_result_ptr = .bin,
.condbr = .pl_node,
.condbr_inline = .pl_node,
.struct_decl = .pl_node,
.struct_decl_packed = .pl_node,
.struct_decl_extern = .pl_node,
.union_decl = .pl_node,
.union_decl_packed = .pl_node,
.union_decl_extern = .pl_node,
.enum_decl = .pl_node,
.enum_decl_nonexhaustive = .pl_node,
.opaque_decl = .pl_node,
.error_set_decl = .pl_node,
.dbg_stmt_node = .node,
.decl_ref = .str_tok,
.decl_val = .str_tok,
.load = .un_node,
.div = .pl_node,
.elem_ptr = .bin,
.elem_ptr_node = .pl_node,
.elem_val = .bin,
.elem_val_node = .pl_node,
.ensure_result_used = .un_node,
.ensure_result_non_error = .un_node,
.error_union_type = .pl_node,
.error_value = .str_tok,
.@"export" = .pl_node,
.field_ptr = .pl_node,
.field_val = .pl_node,
.field_ptr_named = .pl_node,
.field_val_named = .pl_node,
.func = .pl_node,
.func_inferred = .pl_node,
.import = .str_tok,
.int = .int,
.int_big = .str,
.float = .float,
.float128 = .pl_node,
.int_type = .int_type,
.is_non_null = .un_node,
.is_null = .un_node,
.is_non_null_ptr = .un_node,
.is_null_ptr = .un_node,
.is_err = .un_node,
.is_err_ptr = .un_node,
.loop = .pl_node,
.repeat = .node,
.repeat_inline = .node,
.merge_error_sets = .pl_node,
.mod_rem = .pl_node,
.mul = .pl_node,
.mulwrap = .pl_node,
.param_type = .param_type,
.ref = .un_tok,
.ret_node = .un_node,
.ret_coerce = .un_tok,
.ptr_type_simple = .ptr_type_simple,
.ptr_type = .ptr_type,
.slice_start = .pl_node,
.slice_end = .pl_node,
.slice_sentinel = .pl_node,
.store = .bin,
.store_node = .pl_node,
.store_to_block_ptr = .bin,
.store_to_inferred_ptr = .bin,
.str = .str,
.sub = .pl_node,
.subwrap = .pl_node,
.negate = .un_node,
.negate_wrap = .un_node,
.typeof = .un_tok,
.typeof_elem = .un_node,
.typeof_log2_int_type = .un_node,
.log2_int_type = .un_node,
.@"unreachable" = .@"unreachable",
.xor = .pl_node,
.optional_type = .un_node,
.optional_payload_safe = .un_node,
.optional_payload_unsafe = .un_node,
.optional_payload_safe_ptr = .un_node,
.optional_payload_unsafe_ptr = .un_node,
.err_union_payload_safe = .un_node,
.err_union_payload_unsafe = .un_node,
.err_union_payload_safe_ptr = .un_node,
.err_union_payload_unsafe_ptr = .un_node,
.err_union_code = .un_node,
.err_union_code_ptr = .un_node,
.ensure_err_payload_void = .un_tok,
.enum_literal = .str_tok,
.switch_block = .pl_node,
.switch_block_multi = .pl_node,
.switch_block_else = .pl_node,
.switch_block_else_multi = .pl_node,
.switch_block_under = .pl_node,
.switch_block_under_multi = .pl_node,
.switch_block_ref = .pl_node,
.switch_block_ref_multi = .pl_node,
.switch_block_ref_else = .pl_node,
.switch_block_ref_else_multi = .pl_node,
.switch_block_ref_under = .pl_node,
.switch_block_ref_under_multi = .pl_node,
.switch_capture = .switch_capture,
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
.switch_capture_multi_ref = .switch_capture,
.switch_capture_else = .switch_capture,
.switch_capture_else_ref = .switch_capture,
.validate_struct_init_ptr = .pl_node,
.validate_array_init_ptr = .pl_node,
.struct_init_empty = .un_node,
.field_type = .pl_node,
.field_type_ref = .pl_node,
.struct_init = .pl_node,
.struct_init_ref = .pl_node,
.struct_init_anon = .pl_node,
.struct_init_anon_ref = .pl_node,
.array_init = .pl_node,
.array_init_anon = .pl_node,
.array_init_ref = .pl_node,
.array_init_anon_ref = .pl_node,
.union_init_ptr = .pl_node,
.type_info = .un_node,
.size_of = .un_node,
.bit_size_of = .un_node,
.fence = .node,
.ptr_to_int = .un_node,
.error_to_int = .un_node,
.int_to_error = .un_node,
.compile_error = .un_node,
.set_eval_branch_quota = .un_node,
.enum_to_int = .un_node,
.align_of = .un_node,
.bool_to_int = .un_node,
.embed_file = .un_node,
.error_name = .un_node,
.panic = .un_node,
.set_align_stack = .un_node,
.set_cold = .un_node,
.set_float_mode = .un_node,
.set_runtime_safety = .un_node,
.sqrt = .un_node,
.sin = .un_node,
.cos = .un_node,
.exp = .un_node,
.exp2 = .un_node,
.log = .un_node,
.log2 = .un_node,
.log10 = .un_node,
.fabs = .un_node,
.floor = .un_node,
.ceil = .un_node,
.trunc = .un_node,
.round = .un_node,
.tag_name = .un_node,
.reify = .un_node,
.type_name = .un_node,
.frame_type = .un_node,
.frame_size = .un_node,
.float_to_int = .pl_node,
.int_to_float = .pl_node,
.int_to_ptr = .pl_node,
.int_to_enum = .pl_node,
.float_cast = .pl_node,
.int_cast = .pl_node,
.err_set_cast = .pl_node,
.ptr_cast = .pl_node,
.truncate = .pl_node,
.align_cast = .pl_node,
.has_decl = .pl_node,
.has_field = .pl_node,
.clz = .un_node,
.ctz = .un_node,
.pop_count = .un_node,
.byte_swap = .un_node,
.bit_reverse = .un_node,
.div_exact = .pl_node,
.div_floor = .pl_node,
.div_trunc = .pl_node,
.mod = .pl_node,
.rem = .pl_node,
.shl = .pl_node,
.shl_exact = .pl_node,
.shr = .pl_node,
.shr_exact = .pl_node,
.bit_offset_of = .pl_node,
.byte_offset_of = .pl_node,
.cmpxchg_strong = .pl_node,
.cmpxchg_weak = .pl_node,
.splat = .pl_node,
.reduce = .pl_node,
.shuffle = .pl_node,
.atomic_load = .pl_node,
.atomic_rmw = .pl_node,
.atomic_store = .pl_node,
.mul_add = .pl_node,
.builtin_call = .pl_node,
.field_ptr_type = .bin,
.field_parent_ptr = .pl_node,
.memcpy = .pl_node,
.memset = .pl_node,
.builtin_async_call = .pl_node,
.c_import = .pl_node,
.alloc = .un_node,
.alloc_mut = .un_node,
.alloc_comptime = .un_node,
.alloc_inferred = .node,
.alloc_inferred_mut = .node,
.alloc_inferred_comptime = .node,
.resolve_inferred_alloc = .un_node,
.@"resume" = .un_node,
.@"await" = .un_node,
.await_nosuspend = .un_node,
.extended = .extended,
});
};
}; };
/// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum. /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
@ -1842,6 +2125,33 @@ pub const Inst = struct {
assert(@sizeOf(Data) == 8); assert(@sizeOf(Data) == 8);
} }
} }
/// TODO this has to be kept in sync with `Data` which we want to be an untagged
/// union. There is some kind of language awkwardness here and it has to do with
/// deserializing an untagged union (in this case `Data`) from a file, and trying
/// to preserve the hidden safety field.
pub const FieldEnum = enum {
extended,
un_node,
un_tok,
pl_node,
bin,
str,
str_tok,
tok,
node,
int,
float,
array_type_sentinel,
ptr_type_simple,
ptr_type,
int_type,
bool_br,
param_type,
@"unreachable",
@"break",
switch_capture,
};
}; };
/// Trailing: /// Trailing:

View File

@ -3606,7 +3606,7 @@ pub fn cmdAstgen(
if (file.zir.hasCompileErrors()) { if (file.zir.hasCompileErrors()) {
var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
try Compilation.AllErrors.addZir(arena, &errors, &file, source); try Compilation.AllErrors.addZir(arena, &errors, &file);
const ttyconf = std.debug.detectTTYConfig(); const ttyconf = std.debug.detectTTYConfig();
for (errors.items) |full_err_msg| { for (errors.items) |full_err_msg| {
full_err_msg.renderToStdErr(ttyconf); full_err_msg.renderToStdErr(ttyconf);