DoxigAlpha

print

Function parameters

Parameters

#
self:*Builder
w:*Writer

Type definitions in this namespace

Types

#

Functions in this namespace

Functions

#

= 16

Values

#

Source

Implementation

#
pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void {
    var need_newline = false;
    var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
    defer metadata_formatter.map.deinit(self.gpa);

    if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        if (self.source_filename != .none) try w.print(
            \\; ModuleID = '{s}'
            \\source_filename = {f}
            \\
        , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) });
        if (self.data_layout != .none) try w.print(
            \\target datalayout = {f}
            \\
        , .{self.data_layout.fmtQ(self)});
        if (self.target_triple != .none) try w.print(
            \\target triple = {f}
            \\
        , .{self.target_triple.fmtQ(self)});
    }

    if (self.module_asm.items.len > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n');
        while (line_it.next()) |line| {
            try w.writeAll("module asm ");
            try printEscapedString(line, .always_quote, w);
            try w.writeByte('\n');
        }
    }

    if (self.types.count() > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        for (self.types.keys(), self.types.values()) |id, ty| try w.print(
            \\%{f} = type {f}
            \\
        , .{ id.fmt(self), ty.fmt(self, .default) });
    }

    if (self.variables.items.len > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        for (self.variables.items) |variable| {
            if (variable.global.getReplacement(self) != .none) continue;
            const global = variable.global.ptrConst(self);
            metadata_formatter.need_comma = true;
            defer metadata_formatter.need_comma = undefined;
            try w.print(
                \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f}
                \\
            , .{
                variable.global.fmt(self),
                Linkage.fmtOptional(
                    if (global.linkage == .external and variable.init != .no_init) null else global.linkage,
                ),
                global.preemption,
                global.visibility,
                global.dll_storage_class,
                variable.thread_local.fmt(" "),
                global.unnamed_addr,
                global.addr_space.fmt(" "),
                global.externally_initialized,
                @tagName(variable.mutability),
                global.type.fmt(self, .percent),
                variable.init.fmt(self, .{ .space = true }),
                variable.alignment.fmt(", "),
                try metadata_formatter.fmt("!dbg ", global.dbg, null),
            });
        }
    }

    if (self.aliases.items.len > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        for (self.aliases.items) |alias| {
            if (alias.global.getReplacement(self) != .none) continue;
            const global = alias.global.ptrConst(self);
            metadata_formatter.need_comma = true;
            defer metadata_formatter.need_comma = undefined;
            try w.print(
                \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f}
                \\
            , .{
                alias.global.fmt(self),
                global.linkage,
                global.preemption,
                global.visibility,
                global.dll_storage_class,
                alias.thread_local.fmt(" "),
                global.unnamed_addr,
                global.type.fmt(self, .percent),
                alias.aliasee.fmt(self, .{ .percent = true }),
                try metadata_formatter.fmt("!dbg ", global.dbg, null),
            });
        }
    }

    var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .empty;
    defer attribute_groups.deinit(self.gpa);

    for (0.., self.functions.items) |function_i, function| {
        if (function.global.getReplacement(self) != .none) continue;
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        const function_index: Function.Index = @enumFromInt(function_i);
        const global = function.global.ptrConst(self);
        const params_len = global.type.functionParameters(self).len;
        const function_attributes = function.attributes.func(self);
        if (function_attributes != .none) try w.print(
            \\; Function Attrs:{f}
            \\
        , .{function_attributes.fmt(self, .{})});
        try w.print(
            \\{s}{f}{f}{f}{f}{f}{f} {f} {f}(
        , .{
            if (function.instructions.len > 0) "define" else "declare",
            global.linkage,
            global.preemption,
            global.visibility,
            global.dll_storage_class,
            function.call_conv,
            function.attributes.ret(self).fmt(self, .{}),
            global.type.functionReturn(self).fmt(self, .percent),
            function.global.fmt(self),
        });
        for (0..params_len) |arg| {
            if (arg > 0) try w.writeAll(", ");
            try w.print(
                \\{f}{f}
            , .{
                global.type.functionParameters(self)[arg].fmt(self, .percent),
                function.attributes.param(arg, self).fmt(self, .{}),
            });
            if (function.instructions.len > 0)
                try w.print(" {f}", .{function.arg(@intCast(arg)).fmt(function_index, self, .{})})
            else
                try w.print(" %{d}", .{arg});
        }
        switch (global.type.functionKind(self)) {
            .normal => {},
            .vararg => {
                if (params_len > 0) try w.writeAll(", ");
                try w.writeAll("...");
            },
        }
        try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") });
        if (function_attributes != .none) try w.print(" #{d}", .{
            (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index,
        });
        {
            metadata_formatter.need_comma = false;
            defer metadata_formatter.need_comma = undefined;
            try w.print("{f}{f}", .{
                function.alignment.fmt(" "),
                try metadata_formatter.fmt(" !dbg ", global.dbg, null),
            });
        }
        if (function.instructions.len > 0) {
            var block_incoming_len: u32 = undefined;
            try w.writeAll(" {\n");
            var maybe_dbg_index: ?u32 = null;
            for (params_len..function.instructions.len) |instruction_i| {
                const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i);
                const instruction = function.instructions.get(@intFromEnum(instruction_index));
                if (function.debug_locations.get(instruction_index)) |debug_location| switch (debug_location) {
                    .no_location => maybe_dbg_index = null,
                    .location => |location| {
                        const gop = try metadata_formatter.map.getOrPut(self.gpa, .{
                            .debug_location = location,
                        });
                        maybe_dbg_index = @intCast(gop.index);
                    },
                };
                switch (instruction.tag) {
                    .add,
                    .@"add nsw",
                    .@"add nuw",
                    .@"add nuw nsw",
                    .@"and",
                    .ashr,
                    .@"ashr exact",
                    .fadd,
                    .@"fadd fast",
                    .@"fcmp false",
                    .@"fcmp fast false",
                    .@"fcmp fast oeq",
                    .@"fcmp fast oge",
                    .@"fcmp fast ogt",
                    .@"fcmp fast ole",
                    .@"fcmp fast olt",
                    .@"fcmp fast one",
                    .@"fcmp fast ord",
                    .@"fcmp fast true",
                    .@"fcmp fast ueq",
                    .@"fcmp fast uge",
                    .@"fcmp fast ugt",
                    .@"fcmp fast ule",
                    .@"fcmp fast ult",
                    .@"fcmp fast une",
                    .@"fcmp fast uno",
                    .@"fcmp oeq",
                    .@"fcmp oge",
                    .@"fcmp ogt",
                    .@"fcmp ole",
                    .@"fcmp olt",
                    .@"fcmp one",
                    .@"fcmp ord",
                    .@"fcmp true",
                    .@"fcmp ueq",
                    .@"fcmp uge",
                    .@"fcmp ugt",
                    .@"fcmp ule",
                    .@"fcmp ult",
                    .@"fcmp une",
                    .@"fcmp uno",
                    .fdiv,
                    .@"fdiv fast",
                    .fmul,
                    .@"fmul fast",
                    .frem,
                    .@"frem fast",
                    .fsub,
                    .@"fsub fast",
                    .@"icmp eq",
                    .@"icmp ne",
                    .@"icmp sge",
                    .@"icmp sgt",
                    .@"icmp sle",
                    .@"icmp slt",
                    .@"icmp uge",
                    .@"icmp ugt",
                    .@"icmp ule",
                    .@"icmp ult",
                    .lshr,
                    .@"lshr exact",
                    .mul,
                    .@"mul nsw",
                    .@"mul nuw",
                    .@"mul nuw nsw",
                    .@"or",
                    .sdiv,
                    .@"sdiv exact",
                    .srem,
                    .shl,
                    .@"shl nsw",
                    .@"shl nuw",
                    .@"shl nuw nsw",
                    .sub,
                    .@"sub nsw",
                    .@"sub nuw",
                    .@"sub nuw nsw",
                    .udiv,
                    .@"udiv exact",
                    .urem,
                    .xor,
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Binary, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.lhs.fmt(function_index, self, .{ .percent = true }),
                            extra.rhs.fmt(function_index, self, .{}),
                        });
                    },
                    .addrspacecast,
                    .bitcast,
                    .fpext,
                    .fptosi,
                    .fptoui,
                    .fptrunc,
                    .inttoptr,
                    .ptrtoint,
                    .sext,
                    .sitofp,
                    .trunc,
                    .uitofp,
                    .zext,
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Cast, instruction.data);
                        try w.print("  %{f} = {s} {f} to {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.val.fmt(function_index, self, .{ .percent = true }),
                            extra.type.fmt(self, .percent),
                        });
                    },
                    .alloca,
                    .@"alloca inalloca",
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Alloca, instruction.data);
                        try w.print("  %{f} = {s} {f}{f}{f}{f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.type.fmt(self, .percent),
                            Value.fmt(switch (extra.len) {
                                .@"1" => .none,
                                else => extra.len,
                            }, function_index, self, .{
                                .comma = true,
                                .percent = true,
                            }),
                            extra.info.alignment.fmt(", "),
                            extra.info.addr_space.fmt(", "),
                        });
                    },
                    .arg => unreachable,
                    .atomicrmw => |tag| {
                        const extra =
                            function.extraData(Function.Instruction.AtomicRmw, instruction.data);
                        try w.print("  %{f} = {t}{f} {t} {f}, {f}{f}{f}{f}", .{
                            instruction_index.name(&function).fmt(self),
                            tag,
                            extra.info.access_kind.fmt(" "),
                            extra.info.atomic_rmw_operation,
                            extra.ptr.fmt(function_index, self, .{ .percent = true }),
                            extra.val.fmt(function_index, self, .{ .percent = true }),
                            extra.info.sync_scope.fmt(" "),
                            extra.info.success_ordering.fmt(" "),
                            extra.info.alignment.fmt(", "),
                        });
                    },
                    .block => {
                        block_incoming_len = instruction.data;
                        const name = instruction_index.name(&function);
                        if (@intFromEnum(instruction_index) > params_len)
                            try w.writeByte('\n');
                        try w.print("{f}:\n", .{name.fmt(self)});
                        continue;
                    },
                    .br => |tag| {
                        const target: Function.Block.Index = @enumFromInt(instruction.data);
                        try w.print("  {s} {f}", .{
                            @tagName(tag), target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .br_cond => {
                        const extra = function.extraData(Function.Instruction.BrCond, instruction.data);
                        try w.print("  br {f}, {f}, {f}", .{
                            extra.cond.fmt(function_index, self, .{ .percent = true }),
                            extra.then.toInst(&function).fmt(function_index, self, .{ .percent = true }),
                            extra.@"else".toInst(&function).fmt(function_index, self, .{ .percent = true }),
                        });
                        metadata_formatter.need_comma = true;
                        defer metadata_formatter.need_comma = undefined;
                        switch (extra.weights) {
                            .none => {},
                            .unpredictable => try w.writeAll("!unpredictable !{}"),
                            _ => try w.print("{f}", .{
                                try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights))), null),
                            }),
                        }
                    },
                    .call,
                    .@"call fast",
                    .@"musttail call",
                    .@"musttail call fast",
                    .@"notail call",
                    .@"notail call fast",
                    .@"tail call",
                    .@"tail call fast",
                    => |tag| {
                        var extra =
                            function.extraDataTrail(Function.Instruction.Call, instruction.data);
                        const args = extra.trail.next(extra.data.args_len, Value, &function);
                        try w.writeAll("  ");
                        const ret_ty = extra.data.ty.functionReturn(self);
                        switch (ret_ty) {
                            .void => {},
                            else => try w.print("%{f} = ", .{
                                instruction_index.name(&function).fmt(self),
                            }),
                            .none => unreachable,
                        }
                        try w.print("{t}{f}{f}{f} {f} {f}(", .{
                            tag,
                            extra.data.info.call_conv,
                            extra.data.attributes.ret(self).fmt(self, .{}),
                            extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self),
                            switch (extra.data.ty.functionKind(self)) {
                                .normal => ret_ty,
                                .vararg => extra.data.ty,
                            }.fmt(self, .percent),
                            extra.data.callee.fmt(function_index, self, .{}),
                        });
                        for (0.., args) |arg_index, arg| {
                            if (arg_index > 0) try w.writeAll(", ");
                            metadata_formatter.need_comma = false;
                            defer metadata_formatter.need_comma = undefined;
                            try w.print("{f}{f}{f}", .{
                                arg.typeOf(function_index, self).fmt(self, .percent),
                                extra.data.attributes.param(arg_index, self).fmt(self, .{}),
                                try metadata_formatter.fmtLocal(" ", arg, function_index),
                            });
                        }
                        try w.writeByte(')');
                        if (extra.data.info.has_op_bundle_cold) {
                            try w.writeAll(" [ \"cold\"() ]");
                        }
                        const call_function_attributes = extra.data.attributes.func(self);
                        if (call_function_attributes != .none) try w.print(" #{d}", .{
                            (try attribute_groups.getOrPutValue(
                                self.gpa,
                                call_function_attributes,
                                {},
                            )).index,
                        });
                    },
                    .cmpxchg,
                    .@"cmpxchg weak",
                    => |tag| {
                        const extra =
                            function.extraData(Function.Instruction.CmpXchg, instruction.data);
                        try w.print("  %{f} = {t}{f} {f}, {f}, {f}{f}{f}{f}{f}", .{
                            instruction_index.name(&function).fmt(self),
                            tag,
                            extra.info.access_kind.fmt(" "),
                            extra.ptr.fmt(function_index, self, .{ .percent = true }),
                            extra.cmp.fmt(function_index, self, .{ .percent = true }),
                            extra.new.fmt(function_index, self, .{ .percent = true }),
                            extra.info.sync_scope.fmt(" "),
                            extra.info.success_ordering.fmt(" "),
                            extra.info.failure_ordering.fmt(" "),
                            extra.info.alignment.fmt(", "),
                        });
                    },
                    .extractelement => |tag| {
                        const extra =
                            function.extraData(Function.Instruction.ExtractElement, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.val.fmt(function_index, self, .{ .percent = true }),
                            extra.index.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .extractvalue => |tag| {
                        var extra = function.extraDataTrail(
                            Function.Instruction.ExtractValue,
                            instruction.data,
                        );
                        const indices = extra.trail.next(extra.data.indices_len, u32, &function);
                        try w.print("  %{f} = {s} {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.data.val.fmt(function_index, self, .{ .percent = true }),
                        });
                        for (indices) |index| try w.print(", {d}", .{index});
                    },
                    .fence => |tag| {
                        const info: MemoryAccessInfo = @bitCast(instruction.data);
                        try w.print("  {t}{f}{f}", .{
                            tag,
                            info.sync_scope.fmt(" "),
                            info.success_ordering.fmt(" "),
                        });
                    },
                    .fneg,
                    .@"fneg fast",
                    => |tag| {
                        const val: Value = @enumFromInt(instruction.data);
                        try w.print("  %{f} = {s} {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            val.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .getelementptr,
                    .@"getelementptr inbounds",
                    => |tag| {
                        var extra = function.extraDataTrail(
                            Function.Instruction.GetElementPtr,
                            instruction.data,
                        );
                        const indices = extra.trail.next(extra.data.indices_len, Value, &function);
                        try w.print("  %{f} = {s} {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.data.type.fmt(self, .percent),
                            extra.data.base.fmt(function_index, self, .{ .percent = true }),
                        });
                        for (indices) |index| try w.print(", {f}", .{
                            index.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .indirectbr => |tag| {
                        var extra =
                            function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data);
                        const targets =
                            extra.trail.next(extra.data.targets_len, Function.Block.Index, &function);
                        try w.print("  {s} {f}, [", .{
                            @tagName(tag),
                            extra.data.addr.fmt(function_index, self, .{ .percent = true }),
                        });
                        for (0.., targets) |target_index, target| {
                            if (target_index > 0) try w.writeAll(", ");
                            try w.print("{f}", .{
                                target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
                            });
                        }
                        try w.writeByte(']');
                    },
                    .insertelement => |tag| {
                        const extra =
                            function.extraData(Function.Instruction.InsertElement, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.val.fmt(function_index, self, .{ .percent = true }),
                            extra.elem.fmt(function_index, self, .{ .percent = true }),
                            extra.index.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .insertvalue => |tag| {
                        var extra =
                            function.extraDataTrail(Function.Instruction.InsertValue, instruction.data);
                        const indices = extra.trail.next(extra.data.indices_len, u32, &function);
                        try w.print("  %{f} = {s} {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.data.val.fmt(function_index, self, .{ .percent = true }),
                            extra.data.elem.fmt(function_index, self, .{ .percent = true }),
                        });
                        for (indices) |index| try w.print(", {d}", .{index});
                    },
                    .load,
                    .@"load atomic",
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Load, instruction.data);
                        try w.print("  %{f} = {t}{f} {f}, {f}{f}{f}{f}", .{
                            instruction_index.name(&function).fmt(self),
                            tag,
                            extra.info.access_kind.fmt(" "),
                            extra.type.fmt(self, .percent),
                            extra.ptr.fmt(function_index, self, .{ .percent = true }),
                            extra.info.sync_scope.fmt(" "),
                            extra.info.success_ordering.fmt(" "),
                            extra.info.alignment.fmt(", "),
                        });
                    },
                    .phi,
                    .@"phi fast",
                    => |tag| {
                        var extra = function.extraDataTrail(Function.Instruction.Phi, instruction.data);
                        const vals = extra.trail.next(block_incoming_len, Value, &function);
                        const blocks =
                            extra.trail.next(block_incoming_len, Function.Block.Index, &function);
                        try w.print("  %{f} = {s} {f} ", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            vals[0].typeOf(function_index, self).fmt(self, .percent),
                        });
                        for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| {
                            if (incoming_index > 0) try w.writeAll(", ");
                            try w.print("[ {f}, {f} ]", .{
                                incoming_val.fmt(function_index, self, .{}),
                                incoming_block.toInst(&function).fmt(function_index, self, .{}),
                            });
                        }
                    },
                    .ret => |tag| {
                        const val: Value = @enumFromInt(instruction.data);
                        try w.print("  {s} {f}", .{
                            @tagName(tag),
                            val.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .@"ret void",
                    .@"unreachable",
                    => |tag| try w.print("  {s}", .{@tagName(tag)}),
                    .select,
                    .@"select fast",
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Select, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.cond.fmt(function_index, self, .{ .percent = true }),
                            extra.lhs.fmt(function_index, self, .{ .percent = true }),
                            extra.rhs.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .shufflevector => |tag| {
                        const extra =
                            function.extraData(Function.Instruction.ShuffleVector, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.lhs.fmt(function_index, self, .{ .percent = true }),
                            extra.rhs.fmt(function_index, self, .{ .percent = true }),
                            extra.mask.fmt(function_index, self, .{ .percent = true }),
                        });
                    },
                    .store,
                    .@"store atomic",
                    => |tag| {
                        const extra = function.extraData(Function.Instruction.Store, instruction.data);
                        try w.print("  {t}{f} {f}, {f}{f}{f}{f}", .{
                            tag,
                            extra.info.access_kind.fmt(" "),
                            extra.val.fmt(function_index, self, .{ .percent = true }),
                            extra.ptr.fmt(function_index, self, .{ .percent = true }),
                            extra.info.sync_scope.fmt(" "),
                            extra.info.success_ordering.fmt(" "),
                            extra.info.alignment.fmt(", "),
                        });
                    },
                    .@"switch" => |tag| {
                        var extra =
                            function.extraDataTrail(Function.Instruction.Switch, instruction.data);
                        const vals = extra.trail.next(extra.data.cases_len, Constant, &function);
                        const blocks =
                            extra.trail.next(extra.data.cases_len, Function.Block.Index, &function);
                        try w.print("  {s} {f}, {f} [\n", .{
                            @tagName(tag),
                            extra.data.val.fmt(function_index, self, .{ .percent = true }),
                            extra.data.default.toInst(&function).fmt(function_index, self, .{ .percent = true }),
                        });
                        for (vals, blocks) |case_val, case_block| try w.print(
                            "    {f}, {f}\n",
                            .{
                                case_val.fmt(self, .{ .percent = true }),
                                case_block.toInst(&function).fmt(function_index, self, .{ .percent = true }),
                            },
                        );
                        try w.writeAll("  ]");
                        metadata_formatter.need_comma = true;
                        defer metadata_formatter.need_comma = undefined;
                        switch (extra.data.weights) {
                            .none => {},
                            .unpredictable => try w.writeAll("!unpredictable !{}"),
                            _ => try w.print("{f}", .{
                                try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights))), null),
                            }),
                        }
                    },
                    .va_arg => |tag| {
                        const extra = function.extraData(Function.Instruction.VaArg, instruction.data);
                        try w.print("  %{f} = {s} {f}, {f}", .{
                            instruction_index.name(&function).fmt(self),
                            @tagName(tag),
                            extra.list.fmt(function_index, self, .{ .percent = true }),
                            extra.type.fmt(self, .percent),
                        });
                    },
                }

                if (maybe_dbg_index) |dbg_index| {
                    try w.print(", !dbg !{d}", .{dbg_index});
                }
                try w.writeByte('\n');
            }
            try w.writeByte('}');
        }
        try w.writeByte('\n');
    }

    if (attribute_groups.count() > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group|
            try w.print(
                \\attributes #{d} = {{{f} }}
                \\
            , .{ attribute_group_index, attribute_group.fmt(self, .{ .pound = true, .quote = true }) });
    }

    if (self.metadata_named.count() > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| {
            const elements: []const Metadata =
                @ptrCast(self.metadata_extra.items[data.index..][0..data.len]);
            try w.writeByte('!');
            try printEscapedString(name.slice(self), .quote_unless_valid_identifier, w);
            try w.writeAll(" = !{");
            metadata_formatter.need_comma = false;
            defer metadata_formatter.need_comma = undefined;
            for (elements) |element| try w.print("{f}", .{try metadata_formatter.fmt("", element, null)});
            try w.writeAll("}\n");
        }
    }

    if (metadata_formatter.map.count() > 0) {
        if (need_newline) try w.writeByte('\n') else need_newline = true;
        var metadata_index: usize = 0;
        while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) {
            @setEvalBranchQuota(10_000);
            try w.print("!{d} = ", .{metadata_index});
            metadata_formatter.need_comma = false;
            defer metadata_formatter.need_comma = undefined;

            const key = metadata_formatter.map.keys()[metadata_index];
            const metadata_item = switch (key) {
                .debug_location => |location| {
                    try metadata_formatter.specialized(.@"!", .DILocation, .{
                        .line = location.line,
                        .column = location.column,
                        .scope = location.scope,
                        .inlinedAt = location.inlined_at,
                        .isImplicitCode = false,
                    }, w);
                    continue;
                },
                .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)),
            };

            switch (metadata_item.tag) {
                .none, .expression, .constant => unreachable,
                .file => {
                    const extra = self.metadataExtraData(Metadata.File, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DIFile, .{
                        .filename = extra.filename,
                        .directory = extra.directory,
                        .checksumkind = null,
                        .checksum = null,
                        .source = null,
                    }, w);
                },
                .compile_unit,
                .@"compile_unit optimized",
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.CompileUnit, metadata_item.data);
                    try metadata_formatter.specialized(.@"distinct !", .DICompileUnit, .{
                        .language = .DW_LANG_C99,
                        .file = extra.file,
                        .producer = extra.producer,
                        .isOptimized = switch (kind) {
                            .compile_unit => false,
                            .@"compile_unit optimized" => true,
                            else => unreachable,
                        },
                        .flags = null,
                        .runtimeVersion = 0,
                        .splitDebugFilename = null,
                        .emissionKind = .FullDebug,
                        .enums = extra.enums,
                        .retainedTypes = null,
                        .globals = extra.globals,
                        .imports = null,
                        .macros = null,
                        .dwoId = null,
                        .splitDebugInlining = false,
                        .debugInfoForProfiling = null,
                        .nameTableKind = null,
                        .rangesBaseAddress = null,
                        .sysroot = null,
                        .sdk = null,
                    }, w);
                },
                .subprogram,
                .@"subprogram local",
                .@"subprogram definition",
                .@"subprogram local definition",
                .@"subprogram optimized",
                .@"subprogram optimized local",
                .@"subprogram optimized definition",
                .@"subprogram optimized local definition",
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.Subprogram, metadata_item.data);
                    try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{
                        .name = extra.name,
                        .linkageName = extra.linkage_name,
                        .scope = extra.file,
                        .file = extra.file,
                        .line = extra.line,
                        .type = extra.ty,
                        .scopeLine = extra.scope_line,
                        .containingType = null,
                        .virtualIndex = null,
                        .thisAdjustment = null,
                        .flags = extra.di_flags,
                        .spFlags = @as(Metadata.Subprogram.DISPFlags, @bitCast(@as(u32, @as(u3, @intCast(
                            @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram),
                        ))) << 2)),
                        .unit = extra.compile_unit,
                        .templateParams = null,
                        .declaration = null,
                        .retainedNodes = null,
                        .thrownTypes = null,
                        .annotations = null,
                        .targetFuncName = null,
                    }, w);
                },
                .lexical_block => {
                    const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data);
                    try metadata_formatter.specialized(.@"distinct !", .DILexicalBlock, .{
                        .scope = extra.scope,
                        .file = extra.file,
                        .line = extra.line,
                        .column = extra.column,
                    }, w);
                },
                .location => {
                    const extra = self.metadataExtraData(Metadata.Location, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DILocation, .{
                        .line = extra.line,
                        .column = extra.column,
                        .scope = extra.scope,
                        .inlinedAt = extra.inlined_at,
                        .isImplicitCode = false,
                    }, w);
                },
                .basic_bool_type,
                .basic_unsigned_type,
                .basic_signed_type,
                .basic_float_type,
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.BasicType, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DIBasicType, .{
                        .tag = null,
                        .name = switch (extra.name) {
                            .none => null,
                            else => extra.name,
                        },
                        .size = extra.bitSize(),
                        .@"align" = null,
                        .encoding = @as(enum {
                            DW_ATE_boolean,
                            DW_ATE_unsigned,
                            DW_ATE_signed,
                            DW_ATE_float,
                        }, switch (kind) {
                            .basic_bool_type => .DW_ATE_boolean,
                            .basic_unsigned_type => .DW_ATE_unsigned,
                            .basic_signed_type => .DW_ATE_signed,
                            .basic_float_type => .DW_ATE_float,
                            else => unreachable,
                        }),
                        .flags = null,
                    }, w);
                },
                .composite_struct_type,
                .composite_union_type,
                .composite_enumeration_type,
                .composite_array_type,
                .composite_vector_type,
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.CompositeType, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DICompositeType, .{
                        .tag = @as(enum {
                            DW_TAG_structure_type,
                            DW_TAG_union_type,
                            DW_TAG_enumeration_type,
                            DW_TAG_array_type,
                        }, switch (kind) {
                            .composite_struct_type => .DW_TAG_structure_type,
                            .composite_union_type => .DW_TAG_union_type,
                            .composite_enumeration_type => .DW_TAG_enumeration_type,
                            .composite_array_type, .composite_vector_type => .DW_TAG_array_type,
                            else => unreachable,
                        }),
                        .name = switch (extra.name) {
                            .none => null,
                            else => extra.name,
                        },
                        .scope = extra.scope,
                        .file = null,
                        .line = null,
                        .baseType = extra.underlying_type,
                        .size = extra.bitSize(),
                        .@"align" = extra.bitAlign(),
                        .offset = null,
                        .flags = null,
                        .elements = extra.fields_tuple,
                        .runtimeLang = null,
                        .vtableHolder = null,
                        .templateParams = null,
                        .identifier = null,
                        .discriminator = null,
                        .dataLocation = null,
                        .associated = null,
                        .allocated = null,
                        .rank = null,
                        .annotations = null,
                    }, w);
                },
                .derived_pointer_type,
                .derived_member_type,
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DIDerivedType, .{
                        .tag = @as(enum {
                            DW_TAG_pointer_type,
                            DW_TAG_member,
                        }, switch (kind) {
                            .derived_pointer_type => .DW_TAG_pointer_type,
                            .derived_member_type => .DW_TAG_member,
                            else => unreachable,
                        }),
                        .name = switch (extra.name) {
                            .none => null,
                            else => extra.name,
                        },
                        .scope = extra.scope,
                        .file = null,
                        .line = null,
                        .baseType = extra.underlying_type,
                        .size = extra.bitSize(),
                        .@"align" = extra.bitAlign(),
                        .offset = switch (extra.bitOffset()) {
                            0 => null,
                            else => |bit_offset| bit_offset,
                        },
                        .flags = null,
                        .extraData = null,
                        .dwarfAddressSpace = null,
                        .annotations = null,
                    }, w);
                },
                .subroutine_type => {
                    const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DISubroutineType, .{
                        .flags = null,
                        .cc = null,
                        .types = extra.types_tuple,
                    }, w);
                },
                .enumerator_unsigned,
                .enumerator_signed_positive,
                .enumerator_signed_negative,
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data);

                    const ExpectedContents = extern struct {
                        const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb));
                        string: [
                            (std.math.big.int.Const{
                                .limbs = &([1]std.math.big.Limb{
                                    std.math.maxInt(std.math.big.Limb),
                                } ** expected_limbs),
                                .positive = false,
                            }).sizeInBaseUpperBound(10)
                        ]u8,
                        limbs: [
                            std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
                        ]std.math.big.Limb,
                    };
                    var stack align(@alignOf(ExpectedContents)) =
                        std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
                    const allocator = stack.get();

                    const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len];
                    const bigint: std.math.big.int.Const = .{
                        .limbs = limbs,
                        .positive = switch (kind) {
                            .enumerator_unsigned,
                            .enumerator_signed_positive,
                            => true,
                            .enumerator_signed_negative => false,
                            else => unreachable,
                        },
                    };
                    const str = try bigint.toStringAlloc(allocator, 10, undefined);
                    defer allocator.free(str);

                    try metadata_formatter.specialized(.@"!", .DIEnumerator, .{
                        .name = extra.name,
                        .value = str,
                        .isUnsigned = switch (kind) {
                            .enumerator_unsigned => true,
                            .enumerator_signed_positive,
                            .enumerator_signed_negative,
                            => false,
                            else => unreachable,
                        },
                    }, w);
                },
                .subrange => {
                    const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DISubrange, .{
                        .count = extra.count,
                        .lowerBound = extra.lower_bound,
                        .upperBound = null,
                        .stride = null,
                    }, w);
                },
                .tuple => {
                    var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data);
                    const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
                    try w.writeAll("!{");
                    for (elements) |element| try w.print("{[element]f}", .{
                        .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
                    });
                    try w.writeAll("}\n");
                },
                .str_tuple => {
                    var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data);
                    const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
                    try w.print("!{{{[str]f}", .{
                        .str = try metadata_formatter.fmt("", extra.data.str, .{ .percent = true }),
                    });
                    for (elements) |element| try w.print("{[element]f}", .{
                        .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
                    });
                    try w.writeAll("}\n");
                },
                .module_flag => {
                    const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data);
                    try w.print("!{{{[behavior]f}{[name]f}{[constant]f}}}\n", .{
                        .behavior = try metadata_formatter.fmt("", extra.behavior, .{ .percent = true }),
                        .name = try metadata_formatter.fmt("", extra.name, .{ .percent = true }),
                        .constant = try metadata_formatter.fmt("", extra.constant, .{ .percent = true }),
                    });
                },
                .local_var => {
                    const extra = self.metadataExtraData(Metadata.LocalVar, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DILocalVariable, .{
                        .name = extra.name,
                        .arg = null,
                        .scope = extra.scope,
                        .file = extra.file,
                        .line = extra.line,
                        .type = extra.ty,
                        .flags = null,
                        .@"align" = null,
                        .annotations = null,
                    }, w);
                },
                .parameter => {
                    const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DILocalVariable, .{
                        .name = extra.name,
                        .arg = extra.arg_no,
                        .scope = extra.scope,
                        .file = extra.file,
                        .line = extra.line,
                        .type = extra.ty,
                        .flags = null,
                        .@"align" = null,
                        .annotations = null,
                    }, w);
                },
                .global_var,
                .@"global_var local",
                => |kind| {
                    const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data);
                    try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{
                        .name = extra.name,
                        .linkageName = extra.linkage_name,
                        .scope = extra.scope,
                        .file = extra.file,
                        .line = extra.line,
                        .type = extra.ty,
                        .isLocal = switch (kind) {
                            .global_var => false,
                            .@"global_var local" => true,
                            else => unreachable,
                        },
                        .isDefinition = true,
                        .declaration = null,
                        .templateParams = null,
                        .@"align" = null,
                        .annotations = null,
                    }, w);
                },
                .global_var_expression => {
                    const extra =
                        self.metadataExtraData(Metadata.GlobalVarExpression, metadata_item.data);
                    try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{
                        .@"var" = extra.variable,
                        .expr = extra.expression,
                    }, w);
                },
            }
        }
    }
}