Commit d70853ba39

Jacob Young <jacobly0@users.noreply.github.com>
2023-05-05 09:04:44
main: add debug dump-zir command
1 parent a111130
Changed files (2)
src/main.zig
@@ -122,6 +122,7 @@ const debug_usage = normal_usage ++
     \\Debug Commands:
     \\
     \\  changelist       Compute mappings from old ZIR to new ZIR
+    \\  dump-zir         Dump a file containing cached ZIR
     \\
 ;
 
@@ -326,6 +327,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
         return cmdAstCheck(gpa, arena, cmd_args);
     } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) {
         return cmdChangelist(gpa, arena, cmd_args);
+    } else if (debug_extensions_enabled and mem.eql(u8, cmd, "dump-zir")) {
+        return cmdDumpZir(gpa, arena, cmd_args);
     } else {
         std.log.info("{s}", .{usage});
         fatal("unknown command: {s}", .{args[1]});
@@ -5579,6 +5582,65 @@ pub fn cmdAstCheck(
     return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
 }
 
+/// This is only enabled for debug builds.
+pub fn cmdDumpZir(
+    gpa: Allocator,
+    arena: Allocator,
+    args: []const []const u8,
+) !void {
+    _ = arena;
+    const Zir = @import("Zir.zig");
+
+    const cache_file = args[0];
+
+    var f = fs.cwd().openFile(cache_file, .{}) catch |err| {
+        fatal("unable to open zir cache file for dumping '{s}': {s}", .{ cache_file, @errorName(err) });
+    };
+    defer f.close();
+
+    var file: Module.File = .{
+        .status = .never_loaded,
+        .source_loaded = false,
+        .tree_loaded = false,
+        .zir_loaded = true,
+        .sub_file_path = undefined,
+        .source = undefined,
+        .stat = undefined,
+        .tree = undefined,
+        .zir = try Module.loadZirCache(gpa, f),
+        .pkg = undefined,
+        .root_decl = .none,
+    };
+
+    {
+        const instruction_bytes = file.zir.instructions.len *
+            // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
+            // the debug safety tag but we want to measure release size.
+            (@sizeOf(Zir.Inst.Tag) + 8);
+        const extra_bytes = file.zir.extra.len * @sizeOf(u32);
+        const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
+            file.zir.string_bytes.len * @sizeOf(u8);
+        const stdout = io.getStdOut();
+        const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
+        // zig fmt: off
+        try stdout.writer().print(
+            \\# Total ZIR bytes:    {}
+            \\# Instructions:       {d} ({})
+            \\# String Table Bytes: {}
+            \\# Extra Data Items:   {d} ({})
+            \\
+        , .{
+            fmtIntSizeBin(total_bytes),
+            file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
+            fmtIntSizeBin(file.zir.string_bytes.len),
+            file.zir.extra.len, fmtIntSizeBin(extra_bytes),
+        });
+        // zig fmt: on
+    }
+
+    return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
+}
+
 /// This is only enabled for debug builds.
 pub fn cmdChangelist(
     gpa: Allocator,
src/Module.zig
@@ -3671,74 +3671,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
                 file.sub_file_path, header.instructions_len,
             });
 
-            var instructions: std.MultiArrayList(Zir.Inst) = .{};
-            defer instructions.deinit(gpa);
-
-            try instructions.setCapacity(gpa, header.instructions_len);
-            instructions.len = 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,
+            file.zir = loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
+                error.UnexpectedFileSize => {
+                    log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
+                    break :update;
                 },
+                else => |e| return e,
             };
-            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});
-                break :update;
-            }
-            if (data_has_safety_tag) {
-                const tags = zir.instructions.items(.tag);
-                for (zir.instructions.items(.data), 0..) |*data, i| {
-                    const union_tag = Zir.Inst.Tag.data_tags[@enumToInt(tags[i])];
-                    const as_struct = @ptrCast(*HackDataLayout, 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,
@@ -3916,6 +3855,76 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
     }
 }
 
+pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
+    return loadZirCacheBody(gpa, try cache_file.reader().readStruct(Zir.Header), cache_file);
+}
+
+fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) !Zir {
+    var instructions: std.MultiArrayList(Zir.Inst) = .{};
+    errdefer instructions.deinit(gpa);
+
+    try instructions.setCapacity(gpa, header.instructions_len);
+    instructions.len = header.instructions_len;
+
+    var zir: Zir = .{
+        .instructions = instructions.toOwnedSlice(),
+        .string_bytes = &.{},
+        .extra = &.{},
+    };
+    errdefer 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) return error.UnexpectedFileSize;
+    if (data_has_safety_tag) {
+        const tags = zir.instructions.items(.tag);
+        for (zir.instructions.items(.data), 0..) |*data, i| {
+            const union_tag = Zir.Inst.Tag.data_tags[@enumToInt(tags[i])];
+            const as_struct = @ptrCast(*HackDataLayout, data);
+            as_struct.* = .{
+                .safety_tag = @enumToInt(union_tag),
+                .data = safety_buffer[i],
+            };
+        }
+    }
+
+    return zir;
+}
+
 /// Patch ups:
 /// * Struct.zir_index
 /// * Decl.zir_index