const Formatter = struct {
builder: *Builder,
need_comma: bool,
map: std.array_hash_map.Auto(union(enum) {
metadata: Metadata,
debug_location: DebugLocation.Location,
}, void) = .empty,
const FormatData = struct {
formatter: *Formatter,
prefix: []const u8 = "",
node: Node,
specialized: ?FormatFlags,
const Node = union(enum) {
none,
@"inline": Metadata,
index: u32,
local_value: ValueData,
local_metadata: ValueData,
local_inline: Metadata,
local_index: u32,
string: Metadata.String,
bool: bool,
u32: u32,
u64: u64,
di_flags: DIFlags,
sp_flags: Subprogram.DISPFlags,
raw: []const u8,
const ValueData = struct {
value: Value,
function: Function.Index,
};
};
};
fn format(data: FormatData, w: *Writer) Writer.Error!void {
if (data.node == .none) return;
const is_specialized = data.specialized != null;
if (data.formatter.need_comma) try w.writeAll(", ");
defer data.formatter.need_comma = true;
try w.writeAll(data.prefix);
const builder = data.formatter.builder;
switch (data.node) {
.none => unreachable,
.@"inline" => |node| {
const needed_comma = data.formatter.need_comma;
defer data.formatter.need_comma = needed_comma;
data.formatter.need_comma = false;
const node_item = node.item(builder);
switch (node_item.tag) {
.expression => {
var extra = builder.metadataExtraDataTrail(Expression, node_item.data);
const elements = extra.trail.next(extra.data.elements_len, u32, builder);
try w.writeAll("!DIExpression(");
for (elements) |element| try format(.{
.formatter = data.formatter,
.node = .{ .u64 = element },
.specialized = .{ .percent = true },
}, w);
try w.writeByte(')');
},
.constant => try Constant.format(.{
.constant = @fromBackingInt(node_item.data),
.builder = builder,
.flags = data.specialized orelse .{},
}, w),
else => unreachable,
}
},
.index => |node| try w.print("!{d}", .{node}),
inline .local_value, .local_metadata => |node, node_tag| try Value.format(.{
.value = node.value,
.function = node.function,
.builder = builder,
.flags = switch (node_tag) {
.local_value => data.specialized orelse .{},
.local_metadata => .{ .percent = true },
else => unreachable,
},
}, w),
inline .local_inline, .local_index => |node, node_tag| {
if (data.specialized) |flags| {
if (flags.onlyPercent()) {
try w.print("{f} ", .{Type.metadata.fmt(builder, .percent)});
}
}
try format(.{
.formatter = data.formatter,
.node = @unionInit(FormatData.Node, @tagName(node_tag)["local_".len..], node),
.specialized = .{ .percent = true },
}, w);
},
.string => |s| {
if (is_specialized) try w.writeByte('!');
try w.print("{f}", .{s.fmt(builder)});
},
inline .bool, .u32, .u64 => |node| try w.print("{}", .{node}),
inline .di_flags, .sp_flags => |node| try w.print("{f}", .{node}),
.raw => |node| try w.writeAll(node),
}
}
inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype, special: ?FormatFlags) switch (@TypeOf(node)) {
Metadata, Metadata.Optional, ?Metadata => Allocator.Error,
else => error{},
}!std.fmt.Alt(FormatData, format) {
const Node = @TypeOf(node);
const MaybeNode = switch (Node) {
Metadata.Optional => ?Metadata,
Metadata.String.Optional => ?Metadata.String,
else => switch (@typeInfo(Node)) {
.optional => Node,
.null => ?noreturn,
else => ?Node,
},
};
const Some = @typeInfo(MaybeNode).optional.child;
return .{ .data = .{
.formatter = formatter,
.prefix = prefix,
.node = if (@as(MaybeNode, switch (Node) {
Metadata.Optional, Metadata.String.Optional => node.unwrap(),
else => node,
})) |some| switch (@typeInfo(Some)) {
.@"enum" => |enum_info| switch (Some) {
Metadata.String => .{ .string = some },
else => if (enum_info.mode == .exhaustive)
.{ .raw = @tagName(some) }
else
@compileError("unknown type to format: " ++ @typeName(Node)),
},
.enum_literal => .{ .raw = @tagName(some) },
.bool => .{ .bool = some },
.@"struct" => switch (Some) {
DIFlags => .{ .di_flags = some },
Metadata => switch (some.kind) {
.string => .{ .string = some.toString() },
.node, .forward => try formatter.refUnwrapped(some.unwrap(formatter.builder)),
.local => unreachable,
},
Subprogram.DISPFlags => .{ .sp_flags = some },
else => @compileError("unknown type to format: " ++ @typeName(Node)),
},
.int, .comptime_int => .{ .u64 = some },
.pointer => .{ .raw = some },
else => @compileError("unknown type to format: " ++ @typeName(Node)),
} else switch (Node) {
Metadata.Optional, Metadata.String.Optional => .none,
else => switch (@typeInfo(Node)) {
.optional, .null => .none,
else => unreachable,
},
},
.specialized = special,
} };
}
inline fn fmtLocal(
formatter: *Formatter,
prefix: []const u8,
value: Value,
function: Function.Index,
) Allocator.Error!std.fmt.Alt(FormatData, format) {
return .{ .data = .{
.formatter = formatter,
.prefix = prefix,
.node = node: switch (value.unwrap()) {
.instruction, .constant => .{ .local_value = .{
.value = value,
.function = function,
} },
.metadata => |metadata| if (value == .none) .none else {
const unwrapped = metadata.unwrap(formatter.builder);
break :node switch (unwrapped.kind) {
.string, .node => switch (try formatter.refUnwrapped(unwrapped)) {
.@"inline" => |node| .{ .local_inline = node },
.index => |node| .{ .local_index = node },
else => unreachable,
},
.forward => unreachable,
.local => .{ .local_metadata = .{
.value = function.ptrConst(formatter.builder).debug_values[
unwrapped.index
].toValue(),
.function = function,
} },
};
},
},
.specialized = null,
} };
}
fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node {
const builder = formatter.builder;
const unwrapped_metadata = node.unwrap(builder);
switch (unwrapped_metadata.tag(builder)) {
.expression, .constant => return .{ .@"inline" = unwrapped_metadata },
else => |metadata_tag| {
assert(!metadata_tag.isInline());
const gop = try formatter.map.getOrPut(builder.gpa, .{ .metadata = unwrapped_metadata });
return .{ .index = @intCast(gop.index) };
},
}
}
inline fn specialized(
formatter: *Formatter,
distinct: enum { @"!", @"distinct !" },
node: enum {
DIFile,
DICompileUnit,
DISubprogram,
DILexicalBlock,
DILocation,
DIBasicType,
DICompositeType,
DIDerivedType,
DISubroutineType,
DIEnumerator,
DISubrange,
DILocalVariable,
DIGlobalVariable,
DIGlobalVariableExpression,
},
nodes: anytype,
w: *Writer,
) !void {
const names = @typeInfo(@TypeOf(nodes)).@"struct".field_names;
comptime var fmt_str: []const u8 = "{[distinct]s}{[node]s}(";
inline for (names) |name| fmt_str = fmt_str ++ "{[" ++ name ++ "]f}";
fmt_str = fmt_str ++ ")\n";
const field_names = @as([]const []const u8, &.{ "distinct", "node" }) ++ names;
comptime var field_types: [2 + names.len]type = undefined;
@memset(field_types[0..2], []const u8);
@memset(field_types[2..], std.fmt.Alt(FormatData, format));
var fmt_args: @Struct(.auto, null, field_names, &field_types, &@splat(.{})) = undefined;
fmt_args.distinct = @tagName(distinct);
fmt_args.node = @tagName(node);
inline for (names) |name| @field(fmt_args, name) = try formatter.fmt(
name ++ ": ",
@field(nodes, name),
null,
);
try w.print(fmt_str, fmt_args);
}
}