authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-24 04:40:45-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-24 17:57:58-04:00
log935ec9ec6a571010ddc11a9212ff0c93f82d0d74
tree90d31d0fad646e8a8b9c4ffc52fa7190a049b6db
parentc604111e22cb2f41e3151b1062e19035f35a6dec

x86_64: canonicalize each br of a block


5 files changed, 114 insertions(+), 44 deletions(-)

src/arch/x86_64/CodeGen.zig+99-40
...@@ -213,7 +213,15 @@ const StackAllocation = struct {...@@ -213,7 +213,15 @@ const StackAllocation = struct {
213};213};
214214
215const BlockData = struct {215const BlockData = struct {
216 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),216 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
217 branch: ?Branch = null,
218 branch_depth: u32,
219
220 fn deinit(self: *BlockData, gpa: Allocator) void {
221 if (self.branch) |*branch| branch.deinit(gpa);
222 self.relocs.deinit(gpa);
223 self.* = undefined;
224 }
217};225};
218226
219const BigTomb = struct {227const BigTomb = struct {
...@@ -5233,11 +5241,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5233,11 +5241,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
5233 // that death now instead of later as this has an effect on5241 // that death now instead of later as this has an effect on
5234 // whether it needs to be spilled in the branches5242 // whether it needs to be spilled in the branches
5235 if (self.liveness.operandDies(inst, 0)) {5243 if (self.liveness.operandDies(inst, 0)) {
5236 const op_int = @enumToInt(pl_op.operand);5244 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
5237 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
5238 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
5239 self.processDeath(op_index);
5240 }
5241 }5245 }
52425246
5243 // Capture the state of register and stack allocation state so that we can revert to it.5247 // Capture the state of register and stack allocation state so that we can revert to it.
...@@ -5290,10 +5294,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5290,10 +5294,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
5290 for (self.branch_stack.items) |bs| {5294 for (self.branch_stack.items) |bs| {
5291 log.debug("{}", .{bs.fmtDebug()});5295 log.debug("{}", .{bs.fmtDebug()});
5292 }5296 }
5293
5294 log.debug("Then branch: {}", .{then_branch.fmtDebug()});5297 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
5295 log.debug("Else branch: {}", .{else_branch.fmtDebug()});5298 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5296 try self.canonicaliseBranches(true, &then_branch, &else_branch);5299
5300 try self.canonicaliseBranches(true, &then_branch, &else_branch, true);
52975301
5298 // We already took care of pl_op.operand earlier, so we're going5302 // We already took care of pl_op.operand earlier, so we're going
5299 // to pass .none here5303 // to pass .none here
...@@ -5599,11 +5603,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -5599,11 +5603,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
5599}5603}
56005604
5601fn airBlock(self: *Self, inst: Air.Inst.Index) !void {5605fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5602 try self.blocks.putNoClobber(self.gpa, inst, .{5606 // A block is a setup to be able to jump to the end.
5603 // A block is a setup to be able to jump to the end.5607 const branch_depth = @intCast(u32, self.branch_stack.items.len);
5604 .relocs = .{},5608 try self.blocks.putNoClobber(self.gpa, inst, .{ .branch_depth = branch_depth });
5605 });5609 defer {
5606 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);5610 var block_data = self.blocks.fetchRemove(inst).?.value;
5611 block_data.deinit(self.gpa);
5612 }
5613
5614 try self.branch_stack.append(.{});
5615 defer _ = self.branch_stack.pop();
56075616
5608 const ty = self.air.typeOfIndex(inst);5617 const ty = self.air.typeOfIndex(inst);
5609 const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst);5618 const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst);
...@@ -5613,8 +5622,8 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -5613,8 +5622,8 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5613 // this field. Following break instructions will use that MCValue to put5622 // this field. Following break instructions will use that MCValue to put
5614 // their block results.5623 // their block results.
5615 const result: MCValue = if (unused) .dead else .none;5624 const result: MCValue = if (unused) .dead else .none;
5616 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];5625 const branch = &self.branch_stack.items[branch_depth];
5617 branch.inst_table.putAssumeCapacityNoClobber(inst, result);5626 try branch.inst_table.putNoClobber(self.gpa, inst, result);
5618 }5627 }
56195628
5620 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5629 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
...@@ -5622,7 +5631,23 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -5622,7 +5631,23 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5622 const body = self.air.extra[extra.end..][0..extra.data.body_len];5631 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5623 try self.genBody(body);5632 try self.genBody(body);
56245633
5625 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc);5634 const block_data = self.blocks.getPtr(inst).?;
5635 {
5636 const src_branch = block_data.branch orelse self.branch_stack.items[branch_depth];
5637 const dst_branch = &self.branch_stack.items[branch_depth - 1];
5638 try dst_branch.inst_table.ensureUnusedCapacity(self.gpa, src_branch.inst_table.count());
5639 var it = src_branch.inst_table.iterator();
5640 while (it.next()) |entry| {
5641 const tracked_inst = entry.key_ptr.*;
5642 const tracked_value = entry.value_ptr.*;
5643 if (dst_branch.inst_table.fetchPutAssumeCapacity(tracked_inst, tracked_value)) |old_entry| {
5644 self.freeValue(old_entry.value);
5645 }
5646 self.getValue(tracked_value, tracked_inst);
5647 }
5648 }
5649
5650 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
56265651
5627 const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*;5652 const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*;
5628 self.getValue(result, inst);5653 self.getValue(result, inst);
...@@ -5647,11 +5672,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5647,11 +5672,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5647 // that death now instead of later as this has an effect on5672 // that death now instead of later as this has an effect on
5648 // whether it needs to be spilled in the branches5673 // whether it needs to be spilled in the branches
5649 if (self.liveness.operandDies(inst, 0)) {5674 if (self.liveness.operandDies(inst, 0)) {
5650 const op_int = @enumToInt(pl_op.operand);5675 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
5651 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
5652 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
5653 self.processDeath(op_index);
5654 }
5655 }5676 }
56565677
5657 log.debug("airSwitch: %{d}", .{inst});5678 log.debug("airSwitch: %{d}", .{inst});
...@@ -5704,8 +5725,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5704,8 +5725,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5704 errdefer case_branch.deinit(self.gpa);5725 errdefer case_branch.deinit(self.gpa);
57055726
5706 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });5727 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });
5728 const final = case_i == cases_len - 1;
5707 if (prev_branch) |*canon_branch| {5729 if (prev_branch) |*canon_branch| {
5708 try self.canonicaliseBranches(case_i == cases_len - 1, canon_branch, &case_branch);5730 try self.canonicaliseBranches(final, canon_branch, &case_branch, true);
5709 canon_branch.deinit(self.gpa);5731 canon_branch.deinit(self.gpa);
5710 }5732 }
5711 prev_branch = case_branch;5733 prev_branch = case_branch;
...@@ -5740,7 +5762,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5740,7 +5762,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
57405762
5741 log.debug("Else branch: {}", .{else_branch.fmtDebug()});5763 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5742 if (prev_branch) |*canon_branch| {5764 if (prev_branch) |*canon_branch| {
5743 try self.canonicaliseBranches(true, canon_branch, &else_branch);5765 try self.canonicaliseBranches(true, canon_branch, &else_branch, true);
5744 canon_branch.deinit(self.gpa);5766 canon_branch.deinit(self.gpa);
5745 }5767 }
5746 prev_branch = else_branch;5768 prev_branch = else_branch;
...@@ -5756,27 +5778,30 @@ fn canonicaliseBranches(...@@ -5756,27 +5778,30 @@ fn canonicaliseBranches(
5756 update_parent: bool,5778 update_parent: bool,
5757 canon_branch: *Branch,5779 canon_branch: *Branch,
5758 target_branch: *const Branch,5780 target_branch: *const Branch,
5781 comptime assert_same_deaths: bool,
5759) !void {5782) !void {
5760 const parent_branch =5783 const parent_branch =
5761 if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined;5784 if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined;
5762 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
57635785
5764 const target_slice = target_branch.inst_table.entries.slice();5786 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
5765 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {5787 var target_it = target_branch.inst_table.iterator();
5788 while (target_it.next()) |target_entry| {
5789 const target_key = target_entry.key_ptr.*;
5790 const target_value = target_entry.value_ptr.*;
5766 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {5791 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
5767 // The instruction's MCValue is overridden in both branches.5792 // The instruction's MCValue is overridden in both branches.
5768 if (update_parent) {5793 if (update_parent) {
5769 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);5794 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5770 }5795 }
5771 if (target_value == .dead) {5796 if (target_value == .dead) {
5772 assert(canon_entry.value == .dead);5797 if (assert_same_deaths) assert(canon_entry.value == .dead);
5773 continue;5798 continue;
5774 }5799 }
5775 break :blk canon_entry.value;5800 break :blk canon_entry.value;
5776 } else blk: {5801 } else blk: {
5777 if (target_value == .dead) continue;5802 if (target_value == .dead) continue;
5778 // The instruction is only overridden in the else branch.5803 // The instruction is only overridden in the else branch.
5779 // If integer overflows occurs, the question is: why wasn't the instruction marked dead?5804 // If integer overflow occurs, the question is: why wasn't the instruction marked dead?
5780 break :blk self.getResolvedInstValue(target_key).?.*;5805 break :blk self.getResolvedInstValue(target_key).?.*;
5781 };5806 };
5782 log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv });5807 log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv });
...@@ -5786,9 +5811,12 @@ fn canonicaliseBranches(...@@ -5786,9 +5811,12 @@ fn canonicaliseBranches(
5786 self.freeValue(target_value);5811 self.freeValue(target_value);
5787 // TODO track the new register / stack allocation5812 // TODO track the new register / stack allocation
5788 }5813 }
5814
5789 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());5815 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());
5790 const canon_slice = canon_branch.inst_table.entries.slice();5816 var canon_it = canon_branch.inst_table.iterator();
5791 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {5817 while (canon_it.next()) |canon_entry| {
5818 const canon_key = canon_entry.key_ptr.*;
5819 const canon_value = canon_entry.value_ptr.*;
5792 // We already deleted the items from this table that matched the target_branch.5820 // We already deleted the items from this table that matched the target_branch.
5793 // So these are all instructions that are only overridden in the canon branch.5821 // So these are all instructions that are only overridden in the canon branch.
5794 const parent_mcv =5822 const parent_mcv =
...@@ -5821,22 +5849,19 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {...@@ -5821,22 +5849,19 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
5821}5849}
58225850
5823fn airBr(self: *Self, inst: Air.Inst.Index) !void {5851fn airBr(self: *Self, inst: Air.Inst.Index) !void {
5824 const branch = self.air.instructions.items(.data)[inst].br;5852 const br = self.air.instructions.items(.data)[inst].br;
5825 try self.br(inst, branch.block_inst, branch.operand);5853 const block = br.block_inst;
5826 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
5827}
58285854
5829fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
5830 // The first break instruction encounters `.none` here and chooses a5855 // The first break instruction encounters `.none` here and chooses a
5831 // machine code value for the block result, populating this field.5856 // machine code value for the block result, populating this field.
5832 // Following break instructions encounter that value and use it for5857 // Following break instructions encounter that value and use it for
5833 // the location to store their block results.5858 // the location to store their block results.
5834 if (self.getResolvedInstValue(block)) |dst_mcv| {5859 if (self.getResolvedInstValue(block)) |dst_mcv| {
5835 const src_mcv = try self.resolveInst(operand);5860 const src_mcv = try self.resolveInst(br.operand);
5836 switch (dst_mcv.*) {5861 switch (dst_mcv.*) {
5837 .none => {5862 .none => {
5838 const result = result: {5863 const result = result: {
5839 if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result src_mcv;5864 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv;
58405865
5841 const new_mcv = try self.allocRegOrMem(block, true);5866 const new_mcv = try self.allocRegOrMem(block, true);
5842 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);5867 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);
...@@ -5848,16 +5873,50 @@ fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Ins...@@ -5848,16 +5873,50 @@ fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Ins
5848 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),5873 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),
5849 }5874 }
5850 }5875 }
5851 return self.brVoid(block);
5852}
58535876
5854fn brVoid(self: *Self, block: Air.Inst.Index) !void {5877 // Process operand death early so that it is properly accounted for in the Branch below.
5878 if (self.liveness.operandDies(inst, 0)) {
5879 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);
5880 }
5881
5855 const block_data = self.blocks.getPtr(block).?;5882 const block_data = self.blocks.getPtr(block).?;
5883 {
5884 var branch = Branch{};
5885 errdefer branch.deinit(self.gpa);
5886
5887 var branch_i = self.branch_stack.items.len - 1;
5888 while (branch_i >= block_data.branch_depth) : (branch_i -= 1) {
5889 const table = &self.branch_stack.items[branch_i].inst_table;
5890 try branch.inst_table.ensureUnusedCapacity(self.gpa, table.count());
5891 var it = table.iterator();
5892 while (it.next()) |entry| {
5893 const gop = branch.inst_table.getOrPutAssumeCapacity(entry.key_ptr.*);
5894 if (!gop.found_existing) gop.value_ptr.* = entry.value_ptr.*;
5895 }
5896 }
5897
5898 if (block_data.branch) |*prev_branch| {
5899 log.debug("brVoid: %{d}", .{inst});
5900 log.debug("Upper branches:", .{});
5901 for (self.branch_stack.items) |bs| {
5902 log.debug("{}", .{bs.fmtDebug()});
5903 }
5904 log.debug("Prev branch: {}", .{prev_branch.fmtDebug()});
5905 log.debug("Cur branch: {}", .{branch.fmtDebug()});
5906
5907 try self.canonicaliseBranches(false, prev_branch, &branch, false);
5908 prev_branch.deinit(self.gpa);
5909 }
5910 block_data.branch = branch;
5911 }
5912
5856 // Emit a jump with a relocation. It will be patched up after the block ends.5913 // Emit a jump with a relocation. It will be patched up after the block ends.
5857 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);5914 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
5858 // Leave the jump offset undefined5915 // Leave the jump offset undefined
5859 const jmp_reloc = try self.asmJmpReloc(undefined);5916 const jmp_reloc = try self.asmJmpReloc(undefined);
5860 block_data.relocs.appendAssumeCapacity(jmp_reloc);5917 block_data.relocs.appendAssumeCapacity(jmp_reloc);
5918
5919 self.finishAirBookkeeping();
5861}5920}
58625921
5863fn airAsm(self: *Self, inst: Air.Inst.Index) !void {5922fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
test/behavior/cast.zig-2
...@@ -38,8 +38,6 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {...@@ -38,8 +38,6 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
38}38}
3939
40test "resolve undefined with integer" {40test "resolve undefined with integer" {
41 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
42
43 try testResolveUndefWithInt(true, 1234);41 try testResolveUndefWithInt(true, 1234);
44 comptime try testResolveUndefWithInt(true, 1234);42 comptime try testResolveUndefWithInt(true, 1234);
45}43}
test/behavior/enum.zig-1
...@@ -904,7 +904,6 @@ test "enum literal casting to tagged union" {...@@ -904,7 +904,6 @@ test "enum literal casting to tagged union" {
904 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;904 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
905 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;905 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
906 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO906 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
907 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
908907
909 const Arch = union(enum) {908 const Arch = union(enum) {
910 x86_64,909 x86_64,
test/behavior/if.zig-1
...@@ -115,7 +115,6 @@ test "if peer expressions inferred optional type" {...@@ -115,7 +115,6 @@ test "if peer expressions inferred optional type" {
115 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;115 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
117 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO117 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
118 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
119118
120 var self: []const u8 = "abcdef";119 var self: []const u8 = "abcdef";
121 var index: usize = 0;120 var index: usize = 0;
tools/lldb_pretty_printers.py+15
...@@ -164,6 +164,20 @@ class zig_ErrorUnion_SynthProvider:...@@ -164,6 +164,20 @@ class zig_ErrorUnion_SynthProvider:
164 def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1164 def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1
165 def get_child_at_index(self, index): return self.payload or self.error_set if index == 0 else None165 def get_child_at_index(self, index): return self.payload or self.error_set if index == 0 else None
166166
167class zig_TaggedUnion_SynthProvider:
168 def __init__(self, value, _=None): self.value = value
169 def update(self):
170 try:
171 self.tag = self.value.GetChildMemberWithName('tag')
172 self.payload = self.value.GetChildMemberWithName('payload').GetChildMemberWithName(self.tag.value)
173 except: pass
174 def has_children(self): return True
175 def num_children(self): return 1 + (self.payload is not None)
176 def get_child_index(self, name):
177 try: return ('tag', 'payload').index(name)
178 except: return -1
179 def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index >= 0 and index < 2 else None
180
167# Define Zig Standard Library181# Define Zig Standard Library
168182
169class std_SegmentedList_SynthProvider:183class std_SegmentedList_SynthProvider:
...@@ -606,3 +620,4 @@ def __lldb_init_module(debugger, _=None):...@@ -606,3 +620,4 @@ def __lldb_init_module(debugger, _=None):
606 add(debugger, category='zig.stage2', type='type.Type', summary=True)620 add(debugger, category='zig.stage2', type='type.Type', summary=True)
607 add(debugger, category='zig.stage2', type='value.Value', identifier='TagOrPayloadPtr', synth=True)621 add(debugger, category='zig.stage2', type='value.Value', identifier='TagOrPayloadPtr', synth=True)
608 add(debugger, category='zig.stage2', type='value.Value', summary=True)622 add(debugger, category='zig.stage2', type='value.Value', summary=True)
623 add(debugger, category='zig.stage2', type='arch.x86_64.CodeGen.MCValue', identifier='zig_TaggedUnion', synth=True, inline_children=True, summary=True)