Commit 0f820d0bdf

Veikka Tuominen <git@vexu.eu>
2022-06-09 16:54:32
stage2: improve debugging tools
llvm: dump failed module when -femit-llvm-ir set print_air: * print fully qualified name * use Type.fmt and Value.fmtValue, fmtDebug is useless TypedValue * handle anon structs and tuples * fix bugs
1 parent 002df65
src/codegen/llvm/bindings.zig
@@ -390,6 +390,9 @@ pub const Module = opaque {
 
     pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2;
     extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void;
+
+    pub const printModuleToFile = LLVMPrintModuleToFile;
+    extern fn LLVMPrintModuleToFile(M: *const Module, Filename: [*:0]const u8, ErrorMessage: *[*:0]const u8) Bool;
 };
 
 pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
src/codegen/llvm.zig
@@ -599,6 +599,13 @@ pub const Object = struct {
             self.llvm_module.dump();
         }
 
+        var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+        defer arena_allocator.deinit();
+        const arena = arena_allocator.allocator();
+
+        const mod = comp.bin_file.options.module.?;
+        const cache_dir = mod.zig_cache_artifact_directory;
+
         if (std.debug.runtime_safety) {
             var error_message: [*:0]const u8 = undefined;
             // verifyModule always allocs the error_message even if there is no error
@@ -606,17 +613,15 @@ pub const Object = struct {
 
             if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
                 std.debug.print("\n{s}\n", .{error_message});
+
+                if (try locPath(arena, comp.emit_llvm_ir, cache_dir)) |emit_llvm_ir_path| {
+                    _ = self.llvm_module.printModuleToFile(emit_llvm_ir_path, &error_message);
+                }
+
                 @panic("LLVM module verification failed");
             }
         }
 
-        var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
-        defer arena_allocator.deinit();
-        const arena = arena_allocator.allocator();
-
-        const mod = comp.bin_file.options.module.?;
-        const cache_dir = mod.zig_cache_artifact_directory;
-
         var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
             try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?))
         else
src/Module.zig
@@ -3790,9 +3790,12 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
             defer liveness.deinit(gpa);
 
             if (builtin.mode == .Debug and mod.comp.verbose_air) {
-                std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
+                const fqn = try decl.getFullyQualifiedName(mod);
+                defer mod.gpa.free(fqn);
+
+                std.debug.print("# Begin Function AIR: {s}:\n", .{fqn});
                 @import("print_air.zig").dump(mod, air, liveness);
-                std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
+                std.debug.print("# End Function AIR: {s}\n\n", .{fqn});
             }
 
             mod.comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) {
src/print_air.zig
@@ -4,6 +4,7 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
 
 const Module = @import("Module.zig");
 const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
 const Air = @import("Air.zig");
 const Liveness = @import("Liveness.zig");
 
@@ -304,14 +305,27 @@ const Writer = struct {
         // no-op, no argument to write
     }
 
+    fn writeType(w: *Writer, s: anytype, ty: Type) !void {
+        const t = ty.tag();
+        switch (t) {
+            .inferred_alloc_const => try s.writeAll("(inferred_alloc_const)"),
+            .inferred_alloc_mut => try s.writeAll("(inferred_alloc_mut)"),
+            .generic_poison => try s.writeAll("(generic_poison)"),
+            .var_args_param => try s.writeAll("(var_args_param)"),
+            .bound_fn => try s.writeAll("(bound_fn)"),
+            else => try ty.print(s, w.module),
+        }
+    }
+
     fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty = w.air.instructions.items(.data)[inst].ty;
-        try s.print("{}", .{ty.fmtDebug()});
+        try w.writeType(s, ty);
     }
 
     fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty_op = w.air.instructions.items(.data)[inst].ty_op;
-        try s.print("{}, ", .{w.air.getRefType(ty_op.ty).fmtDebug()});
+        try w.writeType(s, w.air.getRefType(ty_op.ty));
+        try s.writeAll(", ");
         try w.writeOperand(s, inst, 0, ty_op.operand);
     }
 
@@ -320,7 +334,8 @@ const Writer = struct {
         const extra = w.air.extraData(Air.Block, ty_pl.payload);
         const body = w.air.extra[extra.end..][0..extra.data.body_len];
 
-        try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()});
+        try w.writeType(s, w.air.getRefType(ty_pl.ty));
+        try s.writeAll(", {\n");
         const old_indent = w.indent;
         w.indent += 2;
         try w.writeBody(s, body);
@@ -335,7 +350,8 @@ const Writer = struct {
         const len = @intCast(usize, vector_ty.arrayLen());
         const elements = @ptrCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]);
 
-        try s.print("{}, [", .{vector_ty.fmtDebug()});
+        try w.writeType(s, vector_ty);
+        try s.writeAll(", [");
         for (elements) |elem, i| {
             if (i != 0) try s.writeAll(", ");
             try w.writeOperand(s, inst, i, elem);
@@ -408,7 +424,8 @@ const Writer = struct {
         const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
 
         const elem_ty = w.air.typeOfIndex(inst).childType();
-        try s.print("{}, ", .{elem_ty.fmtDebug()});
+        try w.writeType(s, elem_ty);
+        try s.writeAll(", ");
         try w.writeOperand(s, inst, 0, pl_op.operand);
         try s.writeAll(", ");
         try w.writeOperand(s, inst, 1, extra.lhs);
@@ -511,7 +528,9 @@ const Writer = struct {
     fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
         const val = w.air.values[ty_pl.payload];
-        try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty).fmtDebug(), val.fmtDebug() });
+        const ty = w.air.getRefType(ty_pl.ty);
+        try w.writeType(s, ty);
+        try s.print(", {}", .{val.fmtValue(ty, w.module)});
     }
 
     fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
@@ -523,7 +542,7 @@ const Writer = struct {
         var op_index: usize = 0;
 
         const ret_ty = w.air.typeOfIndex(inst);
-        try s.print("{}", .{ret_ty.fmtDebug()});
+        try w.writeType(s, ret_ty);
 
         if (is_volatile) {
             try s.writeAll(", volatile");
@@ -647,7 +666,10 @@ const Writer = struct {
         const body = w.air.extra[extra.end..][0..extra.data.body_len];
 
         try w.writeOperand(s, inst, 0, extra.data.ptr);
-        try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()});
+
+        try s.writeAll(", ");
+        try w.writeType(s, w.air.getRefType(ty_pl.ty));
+        try s.writeAll(", {\n");
         const old_indent = w.indent;
         w.indent += 2;
         try w.writeBody(s, body);
src/TypedValue.zig
@@ -144,7 +144,41 @@ pub fn print(
                 return writer.writeAll(".{ ... }");
             }
             const vals = val.castTag(.aggregate).?.data;
-            if (ty.zigTypeTag() == .Struct) {
+            if (ty.castTag(.anon_struct)) |anon_struct| {
+                const field_names = anon_struct.data.names;
+                const types = anon_struct.data.types;
+                const max_len = std.math.min(types.len, max_aggregate_items);
+
+                var i: u32 = 0;
+                while (i < max_len) : (i += 1) {
+                    if (i != 0) try writer.writeAll(", ");
+                    try writer.print(".{s} = ", .{field_names[i]});
+                    try print(.{
+                        .ty = types[i],
+                        .val = vals[i],
+                    }, writer, level - 1, mod);
+                }
+                if (types.len > max_aggregate_items) {
+                    try writer.writeAll(", ...");
+                }
+                return writer.writeAll(" }");
+            } else if (ty.isTuple()) {
+                const fields = ty.tupleFields();
+                const max_len = std.math.min(fields.types.len, max_aggregate_items);
+
+                var i: u32 = 0;
+                while (i < max_len) : (i += 1) {
+                    if (i != 0) try writer.writeAll(", ");
+                    try print(.{
+                        .ty = fields.types[i],
+                        .val = vals[i],
+                    }, writer, level - 1, mod);
+                }
+                if (fields.types.len > max_aggregate_items) {
+                    try writer.writeAll(", ...");
+                }
+                return writer.writeAll(" }");
+            } else if (ty.zigTypeTag() == .Struct) {
                 try writer.writeAll(".{ ");
                 const struct_fields = ty.structFields();
                 const len = struct_fields.count();
@@ -194,7 +228,7 @@ pub fn print(
             try writer.writeAll(".{ ");
 
             try print(.{
-                .ty = ty.unionTagType().?,
+                .ty = ty.cast(Type.Payload.Union).?.data.tag_ty,
                 .val = union_val.tag,
             }, writer, level - 1, mod);
             try writer.writeAll(" = ");
@@ -278,19 +312,27 @@ pub fn print(
         .elem_ptr => {
             const elem_ptr = val.castTag(.elem_ptr).?.data;
             try writer.writeAll("&");
-            try print(.{
-                .ty = elem_ptr.elem_ty,
-                .val = elem_ptr.array_ptr,
-            }, writer, level - 1, mod);
+            if (level == 0) {
+                try writer.writeAll("(ptr)");
+            } else {
+                try print(.{
+                    .ty = elem_ptr.elem_ty,
+                    .val = elem_ptr.array_ptr,
+                }, writer, level - 1, mod);
+            }
             return writer.print("[{}]", .{elem_ptr.index});
         },
         .field_ptr => {
             const field_ptr = val.castTag(.field_ptr).?.data;
             try writer.writeAll("&");
-            try print(.{
-                .ty = field_ptr.container_ty,
-                .val = field_ptr.container_ptr,
-            }, writer, level - 1, mod);
+            if (level == 0) {
+                try writer.writeAll("(ptr)");
+            } else {
+                try print(.{
+                    .ty = field_ptr.container_ty,
+                    .val = field_ptr.container_ptr,
+                }, writer, level - 1, mod);
+            }
 
             if (field_ptr.container_ty.zigTypeTag() == .Struct) {
                 const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index];
@@ -344,6 +386,9 @@ pub fn print(
             return writer.writeAll(" }");
         },
         .slice => {
+            if (level == 0) {
+                return writer.writeAll(".{ ... }");
+            }
             const payload = val.castTag(.slice).?.data;
             try writer.writeAll(".{ ");
             const elem_ty = ty.elemType2();
@@ -372,17 +417,25 @@ pub fn print(
         .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
         .eu_payload => {
             val = val.castTag(.eu_payload).?.data;
+            ty = ty.errorUnionPayload();
         },
         .opt_payload => {
             val = val.castTag(.opt_payload).?.data;
+            var buf: Type.Payload.ElemType = undefined;
+            ty = ty.optionalChild(&buf);
+            return print(.{ .ty = ty, .val = val }, writer, level, mod);
         },
         .eu_payload_ptr => {
             try writer.writeAll("&");
             val = val.castTag(.eu_payload_ptr).?.data.container_ptr;
+            ty = ty.elemType2().errorUnionPayload();
         },
         .opt_payload_ptr => {
             try writer.writeAll("&");
-            val = val.castTag(.opt_payload_ptr).?.data.container_ptr;
+            val = val.castTag(.opt_payload).?.data;
+            var buf: Type.Payload.ElemType = undefined;
+            ty = ty.elemType2().optionalChild(&buf);
+            return print(.{ .ty = ty, .val = val }, writer, level, mod);
         },
 
         // TODO these should not appear in this function