authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-25 08:34:57+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-25 08:34:57+01:00
log49e33a2f23c2de4b27cf5ffb4a802ce6cf76f387
treed2b2f1e394e5145bb50fc3b896128490851f2b88
parentf6a2b72ba8b6ab8f8dbef223788c6458af3d4da0
parent4ab4bd04fe8f4308d67b757eaa88f5a356aea688
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15052 from jacobly0/x86-val-tracking

x86_64: fix value tracking bugs

11 files changed, 361 insertions(+), 259 deletions(-)

src/arch/x86_64/CodeGen.zig+338-246
...@@ -213,12 +213,15 @@ const StackAllocation = struct {...@@ -213,12 +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 /// The first break instruction encounters `null` here and chooses a217 branch: Branch = .{},
218 /// machine code value for the block result, populating this field.218 branch_depth: u32,
219 /// Following break instructions encounter that value and use it for219
220 /// the location to store their block results.220 fn deinit(self: *BlockData, gpa: Allocator) void {
221 mcv: MCValue,221 self.branch.deinit(gpa);
222 self.relocs.deinit(gpa);
223 self.* = undefined;
224 }
222};225};
223226
224const BigTomb = struct {227const BigTomb = struct {
...@@ -265,12 +268,15 @@ pub fn generate(...@@ -265,12 +268,15 @@ pub fn generate(
265 const fn_type = fn_owner_decl.ty;268 const fn_type = fn_owner_decl.ty;
266269
267 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);270 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
271 try branch_stack.ensureUnusedCapacity(2);
272 // The outermost branch is used for constants only.
273 branch_stack.appendAssumeCapacity(.{});
274 branch_stack.appendAssumeCapacity(.{});
268 defer {275 defer {
269 assert(branch_stack.items.len == 1);276 assert(branch_stack.items.len == 2);
270 branch_stack.items[0].deinit(bin_file.allocator);277 for (branch_stack.items) |*branch| branch.deinit(bin_file.allocator);
271 branch_stack.deinit();278 branch_stack.deinit();
272 }279 }
273 try branch_stack.append(.{});
274280
275 var function = Self{281 var function = Self{
276 .gpa = bin_file.allocator,282 .gpa = bin_file.allocator,
...@@ -1070,20 +1076,36 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1070,20 +1076,36 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1070 if (self.air_bookkeeping < old_air_bookkeeping + 1) {1076 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
1071 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });1077 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
1072 }1078 }
1079
1080 { // check consistency of tracked registers
1081 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
1082 while (it.next()) |index| {
1083 const tracked_inst = self.register_manager.registers[index];
1084 const tracked_mcv = self.getResolvedInstValue(tracked_inst).?.*;
1085 assert(RegisterManager.indexOfRegIntoTracked(switch (tracked_mcv) {
1086 .register => |reg| reg,
1087 .register_overflow => |ro| ro.reg,
1088 else => unreachable,
1089 }).? == index);
1090 }
1091 }
1073 }1092 }
1074 }1093 }
1075}1094}
10761095
1077/// Asserts there is already capacity to insert into top branch inst_table.1096fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
1078fn processDeath(self: *Self, inst: Air.Inst.Index) void {1097 const reg = switch (value) {
1079 const air_tags = self.air.instructions.items(.tag);1098 .register => |reg| reg,
1080 if (air_tags[inst] == .constant) return; // Constants are immortal.1099 .register_overflow => |ro| ro.reg,
1081 const prev_value = self.getResolvedInstValue(inst) orelse return;1100 else => return,
1082 log.debug("%{d} => {}", .{ inst, MCValue.dead });1101 };
1083 // When editing this function, note that the logic must synchronize with `reuseOperand`.1102 if (self.register_manager.isRegFree(reg)) {
1084 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1103 self.register_manager.getRegAssumeFree(reg, inst);
1085 branch.inst_table.putAssumeCapacity(inst, .dead);1104 }
1086 switch (prev_value) {1105}
1106
1107fn freeValue(self: *Self, value: MCValue) void {
1108 switch (value) {
1087 .register => |reg| {1109 .register => |reg| {
1088 self.register_manager.freeReg(reg);1110 self.register_manager.freeReg(reg);
1089 },1111 },
...@@ -1098,6 +1120,18 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {...@@ -1098,6 +1120,18 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1098 }1120 }
1099}1121}
11001122
1123/// Asserts there is already capacity to insert into top branch inst_table.
1124fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1125 const air_tags = self.air.instructions.items(.tag);
1126 if (air_tags[inst] == .constant) return; // Constants are immortal.
1127 const prev_value = (self.getResolvedInstValue(inst) orelse return).*;
1128 log.debug("%{d} => {}", .{ inst, MCValue.dead });
1129 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1130 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1131 branch.inst_table.putAssumeCapacity(inst, .dead);
1132 self.freeValue(prev_value);
1133}
1134
1101/// Called when there are no operands, and the instruction is always unreferenced.1135/// Called when there are no operands, and the instruction is always unreferenced.
1102fn finishAirBookkeeping(self: *Self) void {1136fn finishAirBookkeeping(self: *Self) void {
1103 if (std.debug.runtime_safety) {1137 if (std.debug.runtime_safety) {
...@@ -1121,32 +1155,21 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -1121,32 +1155,21 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
1121 log.debug("%{d} => {}", .{ inst, result });1155 log.debug("%{d} => {}", .{ inst, result });
1122 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1156 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1123 branch.inst_table.putAssumeCapacityNoClobber(inst, result);1157 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
11241158 // In some cases, an operand may be reused as the result.
1125 // In some cases (such as bitcast), an operand1159 // If that operand died and was a register, it was freed by
1126 // may be the same MCValue as the result. If1160 // processDeath, so we have to "re-allocate" the register.
1127 // that operand died and was a register, it1161 self.getValue(result, inst);
1128 // was freed by processDeath. We have to1162 } else switch (result) {
1129 // "re-allocate" the register.1163 .none, .dead, .unreach => {},
1130 switch (result) {1164 else => unreachable, // Why didn't the result die?
1131 .register => |reg| {
1132 if (self.register_manager.isRegFree(reg)) {
1133 self.register_manager.getRegAssumeFree(reg, inst);
1134 }
1135 },
1136 .register_overflow => |ro| {
1137 if (self.register_manager.isRegFree(ro.reg)) {
1138 self.register_manager.getRegAssumeFree(ro.reg, inst);
1139 }
1140 },
1141 else => {},
1142 }
1143 }1165 }
1144 self.finishAirBookkeeping();1166 self.finishAirBookkeeping();
1145}1167}
11461168
1147fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {1169fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1170 // In addition to the caller's needs, we need enough space to spill every register and eflags.
1148 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;1171 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1149 try table.ensureUnusedCapacity(self.gpa, additional_count);1172 try table.ensureUnusedCapacity(self.gpa, additional_count + self.register_manager.registers.len + 1);
1150}1173}
11511174
1152fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {1175fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
...@@ -1231,42 +1254,29 @@ fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_...@@ -1231,42 +1254,29 @@ fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_
1231}1254}
12321255
1233const State = struct {1256const State = struct {
1234 next_stack_offset: u32,
1235 registers: abi.RegisterManager.TrackedRegisters,1257 registers: abi.RegisterManager.TrackedRegisters,
1236 free_registers: abi.RegisterManager.RegisterBitSet,1258 free_registers: abi.RegisterManager.RegisterBitSet,
1237 eflags_inst: ?Air.Inst.Index,1259 eflags_inst: ?Air.Inst.Index,
1238 stack: std.AutoHashMapUnmanaged(u32, StackAllocation),
1239
1240 fn deinit(state: *State, gpa: Allocator) void {
1241 state.stack.deinit(gpa);
1242 }
1243};1260};
12441261
1245fn captureState(self: *Self) !State {1262fn captureState(self: *Self) State {
1246 return State{1263 return State{
1247 .next_stack_offset = self.next_stack_offset,
1248 .registers = self.register_manager.registers,1264 .registers = self.register_manager.registers,
1249 .free_registers = self.register_manager.free_registers,1265 .free_registers = self.register_manager.free_registers,
1250 .eflags_inst = self.eflags_inst,1266 .eflags_inst = self.eflags_inst,
1251 .stack = try self.stack.clone(self.gpa),
1252 };1267 };
1253}1268}
12541269
1255fn revertState(self: *Self, state: State) void {1270fn revertState(self: *Self, state: State) void {
1256 self.register_manager.registers = state.registers;
1257 self.eflags_inst = state.eflags_inst;1271 self.eflags_inst = state.eflags_inst;
1258
1259 self.stack.deinit(self.gpa);
1260 self.stack = state.stack;
1261
1262 self.next_stack_offset = state.next_stack_offset;
1263 self.register_manager.free_registers = state.free_registers;1272 self.register_manager.free_registers = state.free_registers;
1273 self.register_manager.registers = state.registers;
1264}1274}
12651275
1266pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {1276pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1267 const stack_mcv = try self.allocRegOrMem(inst, false);1277 const stack_mcv = try self.allocRegOrMem(inst, false);
1268 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });1278 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });
1269 const reg_mcv = self.getResolvedInstValue(inst).?;1279 const reg_mcv = self.getResolvedInstValue(inst).?.*;
1270 switch (reg_mcv) {1280 switch (reg_mcv) {
1271 .register => |other| {1281 .register => |other| {
1272 assert(reg.to64() == other.to64());1282 assert(reg.to64() == other.to64());
...@@ -1277,13 +1287,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -1277,13 +1287,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
1277 else => {},1287 else => {},
1278 }1288 }
1279 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1289 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1280 try branch.inst_table.put(self.gpa, inst, stack_mcv);1290 branch.inst_table.putAssumeCapacity(inst, stack_mcv);
1281 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});1291 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
1282}1292}
12831293
1284pub fn spillEflagsIfOccupied(self: *Self) !void {1294pub fn spillEflagsIfOccupied(self: *Self) !void {
1285 if (self.eflags_inst) |inst_to_save| {1295 if (self.eflags_inst) |inst_to_save| {
1286 const mcv = self.getResolvedInstValue(inst_to_save).?;1296 const mcv = self.getResolvedInstValue(inst_to_save).?.*;
1287 const new_mcv = switch (mcv) {1297 const new_mcv = switch (mcv) {
1288 .register_overflow => try self.allocRegOrMem(inst_to_save, false),1298 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
1289 .eflags => try self.allocRegOrMem(inst_to_save, true),1299 .eflags => try self.allocRegOrMem(inst_to_save, true),
...@@ -1294,7 +1304,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {...@@ -1294,7 +1304,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
1294 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });1304 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });
12951305
1296 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1306 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1297 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);1307 branch.inst_table.putAssumeCapacity(inst_to_save, new_mcv);
12981308
1299 self.eflags_inst = null;1309 self.eflags_inst = null;
13001310
...@@ -1347,13 +1357,23 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty...@@ -1347,13 +1357,23 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty
1347}1357}
13481358
1349fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {1359fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1350 const stack_offset = try self.allocMemPtr(inst);1360 const result: MCValue = result: {
1351 return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none });1361 if (self.liveness.isUnused(inst)) break :result .dead;
1362
1363 const stack_offset = try self.allocMemPtr(inst);
1364 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1365 };
1366 return self.finishAir(inst, result, .{ .none, .none, .none });
1352}1367}
13531368
1354fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {1369fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1355 const stack_offset = try self.allocMemPtr(inst);1370 const result: MCValue = result: {
1356 return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none });1371 if (self.liveness.isUnused(inst)) break :result .dead;
1372
1373 const stack_offset = try self.allocMemPtr(inst);
1374 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1375 };
1376 return self.finishAir(inst, result, .{ .none, .none, .none });
1357}1377}
13581378
1359fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {1379fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1992,11 +2012,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1992,11 +2012,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1992 },2012 },
1993 .register => |reg| {2013 .register => |reg| {
1994 // TODO reuse operand2014 // TODO reuse operand
1995 self.register_manager.getRegAssumeFree(.rcx, null);
1996 const rcx_lock =
1997 if (err_off > 0) self.register_manager.lockRegAssumeUnused(.rcx) else null;
1998 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock);
1999
2000 const eu_lock = self.register_manager.lockReg(reg);2015 const eu_lock = self.register_manager.lockReg(reg);
2001 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);2016 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20022017
...@@ -2047,11 +2062,6 @@ fn genUnwrapErrorUnionPayloadMir(...@@ -2047,11 +2062,6 @@ fn genUnwrapErrorUnionPayloadMir(
2047 },2062 },
2048 .register => |reg| {2063 .register => |reg| {
2049 // TODO reuse operand2064 // TODO reuse operand
2050 self.register_manager.getRegAssumeFree(.rcx, null);
2051 const rcx_lock =
2052 if (payload_off > 0) self.register_manager.lockRegAssumeUnused(.rcx) else null;
2053 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock);
2054
2055 const eu_lock = self.register_manager.lockReg(reg);2065 const eu_lock = self.register_manager.lockReg(reg);
2056 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);2066 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20572067
...@@ -2749,7 +2759,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {...@@ -2749,7 +2759,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
2749 };2759 };
2750 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock);2760 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock);
27512761
2752 const dst_reg = try self.register_manager.allocReg(inst, gp);2762 const dst_reg = try self.register_manager.allocReg(null, gp);
2753 const dst_mcv = MCValue{ .register = dst_reg };2763 const dst_mcv = MCValue{ .register = dst_reg };
2754 const dst_lock = self.register_manager.lockReg(dst_reg);2764 const dst_lock = self.register_manager.lockReg(dst_reg);
2755 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);2765 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -2764,14 +2774,14 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {...@@ -2764,14 +2774,14 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
2764 }2774 }
27652775
2766 const src_bits = src_ty.bitSize(self.target.*);2776 const src_bits = src_ty.bitSize(self.target.*);
2767 const width_reg = try self.copyToTmpRegister(dst_ty, .{ .immediate = src_bits });2777 const width_mcv =
2768 const width_mcv = MCValue{ .register = width_reg };2778 try self.copyToRegisterWithInstTracking(inst, dst_ty, .{ .immediate = src_bits });
2769 try self.genBinOpMir(.bsr, src_ty, dst_mcv, mat_src_mcv);2779 try self.genBinOpMir(.bsr, src_ty, dst_mcv, mat_src_mcv);
27702780
2771 const dst_abi_size = @intCast(u32, @max(dst_ty.abiSize(self.target.*), 2));2781 const dst_abi_size = @intCast(u32, @max(dst_ty.abiSize(self.target.*), 2));
2772 try self.asmCmovccRegisterRegister(2782 try self.asmCmovccRegisterRegister(
2773 registerAlias(dst_reg, dst_abi_size),2783 registerAlias(dst_reg, dst_abi_size),
2774 registerAlias(width_reg, dst_abi_size),2784 registerAlias(width_mcv.register, dst_abi_size),
2775 .z,2785 .z,
2776 );2786 );
27772787
...@@ -2835,7 +2845,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {...@@ -2835,7 +2845,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
2835 registerAlias(width_reg, abi_size),2845 registerAlias(width_reg, abi_size),
2836 .z,2846 .z,
2837 );2847 );
2838
2839 break :result dst_mcv;2848 break :result dst_mcv;
2840 };2849 };
2841 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2850 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -2877,17 +2886,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {...@@ -2877,17 +2886,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
2877 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);2886 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
2878 const imm_0000_0001 = Immediate.u(mask / 0b1111_1111);2887 const imm_0000_0001 = Immediate.u(mask / 0b1111_1111);
28792888
2880 const tmp_reg = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2889 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2881 src_mcv.register2890 src_mcv
2882 else2891 else
2883 try self.copyToTmpRegister(src_ty, src_mcv);2892 try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv);
2884 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);2893 const dst_reg = dst_mcv.register;
2885 defer self.register_manager.unlockReg(tmp_lock);
2886
2887 const dst_reg = try self.register_manager.allocReg(inst, gp);
2888 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);2894 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
2889 defer self.register_manager.unlockReg(dst_lock);2895 defer self.register_manager.unlockReg(dst_lock);
28902896
2897 const tmp_reg = try self.register_manager.allocReg(null, gp);
2898 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2899 defer self.register_manager.unlockReg(tmp_lock);
2900
2891 {2901 {
2892 const dst = registerAlias(dst_reg, src_abi_size);2902 const dst = registerAlias(dst_reg, src_abi_size);
2893 const tmp = registerAlias(tmp_reg, src_abi_size);2903 const tmp = registerAlias(tmp_reg, src_abi_size);
...@@ -2896,9 +2906,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {...@@ -2896,9 +2906,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
2896 else2906 else
2897 undefined;2907 undefined;
28982908
2899 // tmp = operand
2900 try self.asmRegisterRegister(.mov, dst, tmp);
2901 // dst = operand2909 // dst = operand
2910 try self.asmRegisterRegister(.mov, tmp, dst);
2911 // tmp = operand
2902 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));2912 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));
2903 // tmp = operand >> 12913 // tmp = operand >> 1
2904 if (src_abi_size > 4) {2914 if (src_abi_size > 4) {
...@@ -2948,7 +2958,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {...@@ -2948,7 +2958,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
2948 }2958 }
2949 // dst = (temp3 * 0x01...01) >> (bits - 8)2959 // dst = (temp3 * 0x01...01) >> (bits - 8)
2950 }2960 }
2951 break :result .{ .register = dst_reg };2961 break :result dst_mcv;
2952 };2962 };
2953 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2963 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2954}2964}
...@@ -3170,8 +3180,8 @@ fn reuseOperand(...@@ -3170,8 +3180,8 @@ fn reuseOperand(
3170 .register => |reg| {3180 .register => |reg| {
3171 // If it's in the registers table, need to associate the register with the3181 // If it's in the registers table, need to associate the register with the
3172 // new instruction.3182 // new instruction.
3173 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {3183 if (!self.register_manager.isRegFree(reg)) {
3174 if (!self.register_manager.isRegFree(reg)) {3184 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
3175 self.register_manager.registers[index] = inst;3185 self.register_manager.registers[index] = inst;
3176 }3186 }
3177 }3187 }
...@@ -3510,7 +3520,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {...@@ -3510,7 +3520,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
3510 const value_ty = self.air.typeOf(bin_op.rhs);3520 const value_ty = self.air.typeOf(bin_op.rhs);
3511 log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value });3521 log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value });
3512 try self.store(ptr, value, ptr_ty, value_ty);3522 try self.store(ptr, value, ptr_ty, value_ty);
3513 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });3523 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
3514}3524}
35153525
3516fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {3526fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
...@@ -3796,8 +3806,6 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue...@@ -3796,8 +3806,6 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue
37963806
3797/// Clobbers .rcx for non-immediate shift value.3807/// Clobbers .rcx for non-immediate shift value.
3798fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void {3808fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void {
3799 assert(reg.to64() != .rcx);
3800
3801 switch (tag) {3809 switch (tag) {
3802 .sal, .sar, .shl, .shr => {},3810 .sal, .sar, .shl, .shr => {},
3803 else => unreachable,3811 else => unreachable,
...@@ -4612,23 +4620,24 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4612,23 +4620,24 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4612 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;4620 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
4613 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);4621 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
46144622
4615 if (self.liveness.isUnused(inst))4623 const result: MCValue = result: {
4616 return self.finishAirBookkeeping();4624 if (self.liveness.isUnused(inst)) break :result .dead;
46174625
4618 const dst_mcv: MCValue = switch (mcv) {4626 const dst_mcv: MCValue = switch (mcv) {
4619 .register => |reg| blk: {4627 .register => |reg| blk: {
4620 self.register_manager.getRegAssumeFree(reg.to64(), inst);4628 self.register_manager.getRegAssumeFree(reg.to64(), inst);
4621 break :blk MCValue{ .register = reg };4629 break :blk MCValue{ .register = reg };
4622 },4630 },
4623 .stack_offset => |off| blk: {4631 .stack_offset => |off| blk: {
4624 const offset = @intCast(i32, self.max_end_stack) - off + 16;4632 const offset = @intCast(i32, self.max_end_stack) - off + 16;
4625 break :blk MCValue{ .stack_offset = -offset };4633 break :blk MCValue{ .stack_offset = -offset };
4626 },4634 },
4627 else => return self.fail("TODO implement arg for {}", .{mcv}),4635 else => return self.fail("TODO implement arg for {}", .{mcv}),
4636 };
4637 try self.genArgDbgInfo(ty, name, dst_mcv);
4638 break :result dst_mcv;
4628 };4639 };
4629 try self.genArgDbgInfo(ty, name, dst_mcv);4640 return self.finishAir(inst, result, .{ .none, .none, .none });
4630
4631 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
4632}4641}
46334642
4634fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {4643fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
...@@ -4924,6 +4933,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4924,6 +4933,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4924 }4933 }
49254934
4926 const result: MCValue = result: {4935 const result: MCValue = result: {
4936 if (self.liveness.isUnused(inst)) break :result .dead;
4937
4927 switch (info.return_value) {4938 switch (info.return_value) {
4928 .register => {4939 .register => {
4929 // Save function return value in a new register4940 // Save function return value in a new register
...@@ -5137,7 +5148,10 @@ fn genTry(...@@ -5137,7 +5148,10 @@ fn genTry(
5137 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);5148 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
5138 try self.genBody(body);5149 try self.genBody(body);
5139 try self.performReloc(reloc);5150 try self.performReloc(reloc);
5140 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);5151 const result = if (self.liveness.isUnused(inst))
5152 .dead
5153 else
5154 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
5141 return result;5155 return result;
5142}5156}
51435157
...@@ -5226,15 +5240,11 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5226,15 +5240,11 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
5226 // that death now instead of later as this has an effect on5240 // that death now instead of later as this has an effect on
5227 // whether it needs to be spilled in the branches5241 // whether it needs to be spilled in the branches
5228 if (self.liveness.operandDies(inst, 0)) {5242 if (self.liveness.operandDies(inst, 0)) {
5229 const op_int = @enumToInt(pl_op.operand);5243 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
5230 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
5231 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
5232 self.processDeath(op_index);
5233 }
5234 }5244 }
52355245
5236 // Capture the state of register and stack allocation state so that we can revert to it.5246 // Capture the state of register and stack allocation state so that we can revert to it.
5237 const saved_state = try self.captureState();5247 const saved_state = self.captureState();
52385248
5239 {5249 {
5240 try self.branch_stack.append(.{});5250 try self.branch_stack.append(.{});
...@@ -5283,12 +5293,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5283,12 +5293,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
5283 for (self.branch_stack.items) |bs| {5293 for (self.branch_stack.items) |bs| {
5284 log.debug("{}", .{bs.fmtDebug()});5294 log.debug("{}", .{bs.fmtDebug()});
5285 }5295 }
5286
5287 log.debug("Then branch: {}", .{then_branch.fmtDebug()});5296 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
5288 log.debug("Else branch: {}", .{else_branch.fmtDebug()});5297 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
52895298
5290 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];5299 try self.canonicaliseBranches(true, &then_branch, &else_branch, true, true);
5291 try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch);
52925300
5293 // We already took care of pl_op.operand earlier, so we're going5301 // We already took care of pl_op.operand earlier, so we're going
5294 // to pass .none here5302 // to pass .none here
...@@ -5423,10 +5431,6 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !...@@ -5423,10 +5431,6 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !
5423 try self.genBinOpMir(.cmp, Type.anyerror, .{ .stack_offset = offset }, .{ .immediate = 0 });5431 try self.genBinOpMir(.cmp, Type.anyerror, .{ .stack_offset = offset }, .{ .immediate = 0 });
5424 },5432 },
5425 .register => |reg| {5433 .register => |reg| {
5426 self.register_manager.getRegAssumeFree(.rcx, null);
5427 const rcx_lock = if (err_off > 0) self.register_manager.lockRegAssumeUnused(.rcx) else null;
5428 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock);
5429
5430 const eu_lock = self.register_manager.lockReg(reg);5434 const eu_lock = self.register_manager.lockReg(reg);
5431 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);5435 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
54325436
...@@ -5598,27 +5602,46 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -5598,27 +5602,46 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
5598}5602}
55995603
5600fn airBlock(self: *Self, inst: Air.Inst.Index) !void {5604fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5601 try self.blocks.putNoClobber(self.gpa, inst, .{5605 // A block is a setup to be able to jump to the end.
5602 // A block is a setup to be able to jump to the end.5606 const branch_depth = @intCast(u32, self.branch_stack.items.len);
5603 .relocs = .{},5607 try self.blocks.putNoClobber(self.gpa, inst, .{ .branch_depth = branch_depth });
5604 // It also acts as a receptacle for break operands.5608 defer {
5605 // Here we use `MCValue.none` to represent a null value so that the first5609 var block_data = self.blocks.fetchRemove(inst).?.value;
5606 // break instruction will choose a MCValue for the block result and overwrite5610 block_data.deinit(self.gpa);
5607 // this field. Following break instructions will use that MCValue to put their5611 }
5608 // block results.
5609 .mcv = .none,
5610 });
5611 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
56125612
5613 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5613 const ty = self.air.typeOfIndex(inst);
5614 const extra = self.air.extraData(Air.Block, ty_pl.payload);5614 const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst);
5615 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5616 try self.genBody(body);
56175615
5618 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc);5616 {
5617 // Here we use `.none` to represent a null value so that the first break
5618 // instruction will choose a MCValue for the block result and overwrite
5619 // this field. Following break instructions will use that MCValue to put
5620 // their block results.
5621 const result: MCValue = if (unused) .dead else .none;
5622 const branch = &self.branch_stack.items[branch_depth - 1];
5623 try branch.inst_table.putNoClobber(self.gpa, inst, result);
5624 }
56195625
5620 const result = self.blocks.getPtr(inst).?.mcv;5626 {
5621 return self.finishAir(inst, result, .{ .none, .none, .none });5627 try self.branch_stack.append(.{});
5628 errdefer _ = self.branch_stack.pop();
5629
5630 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5631 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5632 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5633 try self.genBody(body);
5634 }
5635
5636 const block_data = self.blocks.getPtr(inst).?;
5637 const target_branch = self.branch_stack.pop();
5638 try self.canonicaliseBranches(true, &block_data.branch, &target_branch, false, false);
5639
5640 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
5641
5642 const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*;
5643 self.getValue(result, inst);
5644 self.finishAirBookkeeping();
5622}5645}
56235646
5624fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {5647fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
...@@ -5639,28 +5662,31 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5639,28 +5662,31 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5639 // that death now instead of later as this has an effect on5662 // that death now instead of later as this has an effect on
5640 // whether it needs to be spilled in the branches5663 // whether it needs to be spilled in the branches
5641 if (self.liveness.operandDies(inst, 0)) {5664 if (self.liveness.operandDies(inst, 0)) {
5642 const op_int = @enumToInt(pl_op.operand);5665 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
5643 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
5644 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
5645 self.processDeath(op_index);
5646 }
5647 }5666 }
56485667
5649 var branch_stack = std.ArrayList(Branch).init(self.gpa);5668 log.debug("airSwitch: %{d}", .{inst});
5650 defer {5669 log.debug("Upper branches:", .{});
5651 for (branch_stack.items) |*bs| {5670 for (self.branch_stack.items) |bs| {
5652 bs.deinit(self.gpa);5671 log.debug("{}", .{bs.fmtDebug()});
5653 }
5654 branch_stack.deinit();
5655 }5672 }
5656 try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1);
56575673
5674 var prev_branch: ?Branch = null;
5675 defer if (prev_branch) |*branch| branch.deinit(self.gpa);
5676
5677 // Capture the state of register and stack allocation state so that we can revert to it.
5678 const saved_state = self.captureState();
5679
5680 const cases_len = switch_br.data.cases_len + @boolToInt(switch_br.data.else_body_len > 0);
5658 while (case_i < switch_br.data.cases_len) : (case_i += 1) {5681 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
5659 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);5682 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
5660 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);5683 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
5661 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];5684 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
5662 extra_index = case.end + items.len + case_body.len;5685 extra_index = case.end + items.len + case_body.len;
56635686
5687 // Revert to the previous register and stack allocation state.
5688 if (prev_branch) |_| self.revertState(saved_state);
5689
5664 var relocs = try self.gpa.alloc(u32, items.len);5690 var relocs = try self.gpa.alloc(u32, items.len);
5665 defer self.gpa.free(relocs);5691 defer self.gpa.free(relocs);
56665692
...@@ -5671,12 +5697,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5671,12 +5697,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5671 reloc.* = try self.asmJccReloc(undefined, .ne);5697 reloc.* = try self.asmJccReloc(undefined, .ne);
5672 }5698 }
56735699
5674 // Capture the state of register and stack allocation state so that we can revert to it.
5675 const saved_state = try self.captureState();
5676
5677 {5700 {
5678 try self.branch_stack.append(.{});5701 if (cases_len > 1) try self.branch_stack.append(.{});
5679 errdefer _ = self.branch_stack.pop();5702 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
56805703
5681 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);5704 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
5682 for (liveness.deaths[case_i]) |operand| {5705 for (liveness.deaths[case_i]) |operand| {
...@@ -5686,25 +5709,32 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5686,25 +5709,32 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5686 try self.genBody(case_body);5709 try self.genBody(case_body);
5687 }5710 }
56885711
5689 branch_stack.appendAssumeCapacity(self.branch_stack.pop());5712 // Consolidate returned MCValues between prongs like we do in airCondBr.
56905713 if (cases_len > 1) {
5691 // Revert to the previous register and stack allocation state.5714 var case_branch = self.branch_stack.pop();
5692 self.revertState(saved_state);5715 errdefer case_branch.deinit(self.gpa);
56935716
5694 for (relocs) |reloc| {5717 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });
5695 try self.performReloc(reloc);5718 const final = case_i == cases_len - 1;
5719 if (prev_branch) |*canon_branch| {
5720 try self.canonicaliseBranches(final, canon_branch, &case_branch, true, true);
5721 canon_branch.deinit(self.gpa);
5722 }
5723 prev_branch = case_branch;
5696 }5724 }
5725
5726 for (relocs) |reloc| try self.performReloc(reloc);
5697 }5727 }
56985728
5699 if (switch_br.data.else_body_len > 0) {5729 if (switch_br.data.else_body_len > 0) {
5700 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];5730 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
57015731
5702 // Capture the state of register and stack allocation state so that we can revert to it.5732 // Revert to the previous register and stack allocation state.
5703 const saved_state = try self.captureState();5733 if (prev_branch) |_| self.revertState(saved_state);
57045734
5705 {5735 {
5706 try self.branch_stack.append(.{});5736 if (cases_len > 1) try self.branch_stack.append(.{});
5707 errdefer _ = self.branch_stack.pop();5737 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
57085738
5709 const else_deaths = liveness.deaths.len - 1;5739 const else_deaths = liveness.deaths.len - 1;
5710 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);5740 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
...@@ -5715,78 +5745,103 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5715,78 +5745,103 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5715 try self.genBody(else_body);5745 try self.genBody(else_body);
5716 }5746 }
57175747
5718 branch_stack.appendAssumeCapacity(self.branch_stack.pop());5748 // Consolidate returned MCValues between a prong and the else branch like we do in airCondBr.
57195749 if (cases_len > 1) {
5720 // Revert to the previous register and stack allocation state.5750 var else_branch = self.branch_stack.pop();
5721 self.revertState(saved_state);5751 errdefer else_branch.deinit(self.gpa);
5722 }
57235752
5724 // Consolidate returned MCValues between prongs and else branch like we do5753 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5725 // in airCondBr.5754 if (prev_branch) |*canon_branch| {
5726 log.debug("airSwitch: %{d}", .{inst});5755 try self.canonicaliseBranches(true, canon_branch, &else_branch, true, true);
5727 log.debug("Upper branches:", .{});5756 canon_branch.deinit(self.gpa);
5728 for (self.branch_stack.items) |bs| {5757 }
5729 log.debug("{}", .{bs.fmtDebug()});5758 prev_branch = else_branch;
5730 }5759 }
5731 for (branch_stack.items, 0..) |bs, i| {
5732 log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() });
5733 }
5734
5735 // TODO: can we reduce the complexity of this algorithm?
5736 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
5737 var i: usize = branch_stack.items.len;
5738 while (i > 1) : (i -= 1) {
5739 const canon_branch = &branch_stack.items[i - 2];
5740 const target_branch = &branch_stack.items[i - 1];
5741 try self.canonicaliseBranches(parent_branch, canon_branch, target_branch);
5742 }5760 }
57435761
5744 // We already took care of pl_op.operand earlier, so we're going5762 // We already took care of pl_op.operand earlier, so we're going to pass .none here
5745 // to pass .none here
5746 return self.finishAir(inst, .unreach, .{ .none, .none, .none });5763 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
5747}5764}
57485765
5749fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void {5766fn canonicaliseBranches(
5750 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());5767 self: *Self,
5768 update_parent: bool,
5769 canon_branch: *Branch,
5770 target_branch: *const Branch,
5771 comptime set_values: bool,
5772 comptime assert_same_deaths: bool,
5773) !void {
5774 var hazard_map = std.AutoHashMap(MCValue, void).init(self.gpa);
5775 defer hazard_map.deinit();
5776
5777 const parent_branch =
5778 if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined;
57515779
5752 const target_slice = target_branch.inst_table.entries.slice();5780 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
5753 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {5781 var target_it = target_branch.inst_table.iterator();
5782 while (target_it.next()) |target_entry| {
5783 const target_key = target_entry.key_ptr.*;
5784 const target_value = target_entry.value_ptr.*;
5754 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {5785 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
5755 // The instruction's MCValue is overridden in both branches.5786 // The instruction's MCValue is overridden in both branches.
5756 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5757 if (target_value == .dead) {5787 if (target_value == .dead) {
5758 assert(canon_entry.value == .dead);5788 if (update_parent) {
5789 parent_branch.inst_table.putAssumeCapacity(target_key, .dead);
5790 }
5791 if (assert_same_deaths) assert(canon_entry.value == .dead);
5759 continue;5792 continue;
5760 }5793 }
5794 if (update_parent) {
5795 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5796 }
5761 break :blk canon_entry.value;5797 break :blk canon_entry.value;
5762 } else blk: {5798 } else blk: {
5763 if (target_value == .dead)5799 if (target_value == .dead) {
5800 if (update_parent) {
5801 parent_branch.inst_table.putAssumeCapacity(target_key, .dead);
5802 }
5764 continue;5803 continue;
5804 }
5765 // The instruction is only overridden in the else branch.5805 // The instruction is only overridden in the else branch.
5766 // If integer overflows occurs, the question is: why wasn't the instruction marked dead?5806 // If integer overflow occurs, the question is: why wasn't the instruction marked dead?
5767 break :blk self.getResolvedInstValue(target_key).?;5807 break :blk self.getResolvedInstValue(target_key).?.*;
5768 };5808 };
5769 log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv });5809 log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv });
5770 // TODO make sure the destination stack offset / register does not already have something5810 // TODO handle the case where the destination stack offset / register has something
5771 // going on there.5811 // going on there.
5772 try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value);5812 assert(!hazard_map.contains(target_value));
5813 try hazard_map.putNoClobber(canon_mcv, {});
5814 if (set_values) {
5815 try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value);
5816 } else self.getValue(canon_mcv, target_key);
5817 self.freeValue(target_value);
5773 // TODO track the new register / stack allocation5818 // TODO track the new register / stack allocation
5774 }5819 }
5775 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());5820
5776 const canon_slice = canon_branch.inst_table.entries.slice();5821 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());
5777 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {5822 var canon_it = canon_branch.inst_table.iterator();
5823 while (canon_it.next()) |canon_entry| {
5824 const canon_key = canon_entry.key_ptr.*;
5825 const canon_value = canon_entry.value_ptr.*;
5778 // We already deleted the items from this table that matched the target_branch.5826 // We already deleted the items from this table that matched the target_branch.
5779 // So these are all instructions that are only overridden in the canon branch.5827 // So these are all instructions that are only overridden in the canon branch.
5780 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);5828 const parent_mcv =
5781 log.debug("canon_value = {}", .{canon_value});5829 if (canon_value != .dead) self.getResolvedInstValue(canon_key).?.* else undefined;
5782 if (canon_value == .dead)5830 if (canon_value != .dead) {
5783 continue;5831 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
5784 const parent_mcv = self.getResolvedInstValue(canon_key).?;5832 // TODO handle the case where the destination stack offset / register has something
5785 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });5833 // going on there.
5786 // TODO make sure the destination stack offset / register does not already have something5834 assert(!hazard_map.contains(parent_mcv));
5787 // going on there.5835 try hazard_map.putNoClobber(canon_value, {});
5788 try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value);5836 if (set_values) {
5789 // TODO track the new register / stack allocation5837 try self.setRegOrMem(self.air.typeOfIndex(canon_key), canon_value, parent_mcv);
5838 } else self.getValue(canon_value, canon_key);
5839 self.freeValue(parent_mcv);
5840 // TODO track the new register / stack allocation
5841 }
5842 if (update_parent) {
5843 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
5844 }
5790 }5845 }
5791}5846}
57925847
...@@ -5804,42 +5859,79 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {...@@ -5804,42 +5859,79 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
5804}5859}
58055860
5806fn airBr(self: *Self, inst: Air.Inst.Index) !void {5861fn airBr(self: *Self, inst: Air.Inst.Index) !void {
5807 const branch = self.air.instructions.items(.data)[inst].br;5862 const br = self.air.instructions.items(.data)[inst].br;
5808 try self.br(branch.block_inst, branch.operand);5863 const block = br.block_inst;
5809 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });5864
5810}5865 // The first break instruction encounters `.none` here and chooses a
5866 // machine code value for the block result, populating this field.
5867 // Following break instructions encounter that value and use it for
5868 // the location to store their block results.
5869 if (self.getResolvedInstValue(block)) |dst_mcv| {
5870 const src_mcv = try self.resolveInst(br.operand);
5871 switch (dst_mcv.*) {
5872 .none => {
5873 const result = result: {
5874 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv;
58115875
5812fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
5813 const block_data = self.blocks.getPtr(block).?;
5814
5815 if (self.air.typeOf(operand).hasRuntimeBits()) {
5816 const operand_mcv = try self.resolveInst(operand);
5817 const block_mcv = block_data.mcv;
5818 if (block_mcv == .none) {
5819 block_data.mcv = switch (operand_mcv) {
5820 .none, .dead, .unreach => unreachable,
5821 .register, .stack_offset, .memory => operand_mcv,
5822 .eflags, .immediate, .ptr_stack_offset => blk: {
5823 const new_mcv = try self.allocRegOrMem(block, true);5876 const new_mcv = try self.allocRegOrMem(block, true);
5824 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);5877 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);
5825 break :blk new_mcv;5878 break :result new_mcv;
5826 },5879 };
5827 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),5880 dst_mcv.* = result;
5828 };5881 self.freeValue(result);
5829 } else {5882 },
5830 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);5883 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),
5831 }5884 }
5832 }5885 }
5833 return self.brVoid(block);
5834}
58355886
5836fn brVoid(self: *Self, block: Air.Inst.Index) !void {5887 // Process operand death early so that it is properly accounted for in the Branch below.
5888 if (self.liveness.operandDies(inst, 0)) {
5889 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);
5890 }
5891
5837 const block_data = self.blocks.getPtr(block).?;5892 const block_data = self.blocks.getPtr(block).?;
5893 {
5894 var branch = Branch{};
5895 errdefer branch.deinit(self.gpa);
5896
5897 var branch_i = self.branch_stack.items.len - 1;
5898 while (branch_i >= block_data.branch_depth) : (branch_i -= 1) {
5899 const table = &self.branch_stack.items[branch_i].inst_table;
5900 try branch.inst_table.ensureUnusedCapacity(self.gpa, table.count());
5901 var it = table.iterator();
5902 while (it.next()) |entry| {
5903 // This loop could be avoided by tracking inst depth, which
5904 // will be needed later anyway for reusing loop deaths.
5905 var parent_branch_i = block_data.branch_depth - 1;
5906 while (parent_branch_i > 0) : (parent_branch_i -= 1) {
5907 const parent_table = &self.branch_stack.items[parent_branch_i].inst_table;
5908 if (parent_table.contains(entry.key_ptr.*)) break;
5909 } else continue;
5910 const gop = branch.inst_table.getOrPutAssumeCapacity(entry.key_ptr.*);
5911 if (!gop.found_existing) gop.value_ptr.* = entry.value_ptr.*;
5912 }
5913 }
5914
5915 log.debug("airBr: %{d}", .{inst});
5916 log.debug("Upper branches:", .{});
5917 for (self.branch_stack.items) |bs| {
5918 log.debug("{}", .{bs.fmtDebug()});
5919 }
5920 log.debug("Prev branch: {}", .{block_data.branch.fmtDebug()});
5921 log.debug("Cur branch: {}", .{branch.fmtDebug()});
5922
5923 try self.canonicaliseBranches(false, &block_data.branch, &branch, true, false);
5924 block_data.branch.deinit(self.gpa);
5925 block_data.branch = branch;
5926 }
5927
5838 // Emit a jump with a relocation. It will be patched up after the block ends.5928 // Emit a jump with a relocation. It will be patched up after the block ends.
5839 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);5929 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
5840 // Leave the jump offset undefined5930 // Leave the jump offset undefined
5841 const jmp_reloc = try self.asmJmpReloc(undefined);5931 const jmp_reloc = try self.asmJmpReloc(undefined);
5842 block_data.relocs.appendAssumeCapacity(jmp_reloc);5932 block_data.relocs.appendAssumeCapacity(jmp_reloc);
5933
5934 self.finishAirBookkeeping();
5843}5935}
58445936
5845fn airAsm(self: *Self, inst: Air.Inst.Index) !void {5937fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
...@@ -6916,7 +7008,8 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {...@@ -6916,7 +7008,8 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
6916 const pl_op = self.air.instructions.items(.data)[inst].pl_op;7008 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
6917 const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data;7009 const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data;
69187010
6919 const dst_reg = try self.register_manager.allocReg(inst, gp);7011 const unused = self.liveness.isUnused(inst);
7012 const dst_reg = try self.register_manager.allocReg(if (unused) null else inst, gp);
69207013
6921 const ptr_ty = self.air.typeOf(pl_op.operand);7014 const ptr_ty = self.air.typeOf(pl_op.operand);
6922 const ptr_mcv = try self.resolveInst(pl_op.operand);7015 const ptr_mcv = try self.resolveInst(pl_op.operand);
...@@ -6924,7 +7017,6 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {...@@ -6924,7 +7017,6 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
6924 const val_ty = self.air.typeOf(extra.operand);7017 const val_ty = self.air.typeOf(extra.operand);
6925 const val_mcv = try self.resolveInst(extra.operand);7018 const val_mcv = try self.resolveInst(extra.operand);
69267019
6927 const unused = self.liveness.isUnused(inst);
6928 try self.atomicOp(dst_reg, ptr_mcv, val_mcv, ptr_ty, val_ty, unused, extra.op(), extra.ordering());7020 try self.atomicOp(dst_reg, ptr_mcv, val_mcv, ptr_ty, val_ty, unused, extra.op(), extra.ordering());
6929 const result: MCValue = if (unused) .dead else .{ .register = dst_reg };7021 const result: MCValue = if (unused) .dead else .{ .register = dst_reg };
6930 return self.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none });7022 return self.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none });
...@@ -7205,17 +7297,17 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {...@@ -7205,17 +7297,17 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
7205 return gop.value_ptr.*;7297 return gop.value_ptr.*;
7206 },7298 },
7207 .const_ty => unreachable,7299 .const_ty => unreachable,
7208 else => return self.getResolvedInstValue(inst_index).?,7300 else => return self.getResolvedInstValue(inst_index).?.*,
7209 }7301 }
7210}7302}
72117303
7212fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue {7304fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?*MCValue {
7213 // Treat each stack item as a "layer" on top of the previous one.7305 // Treat each stack item as a "layer" on top of the previous one.
7214 var i: usize = self.branch_stack.items.len;7306 var i: usize = self.branch_stack.items.len;
7215 while (true) {7307 while (true) {
7216 i -= 1;7308 i -= 1;
7217 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {7309 if (self.branch_stack.items[i].inst_table.getPtr(inst)) |mcv| {
7218 return if (mcv != .dead) mcv else null;7310 return if (mcv.* != .dead) mcv else null;
7219 }7311 }
7220 }7312 }
7221}7313}
src/arch/x86_64/abi.zig+1-1
...@@ -523,7 +523,7 @@ pub fn getCAbiIntReturnRegs(target: Target) []const Register {...@@ -523,7 +523,7 @@ pub fn getCAbiIntReturnRegs(target: Target) []const Register {
523}523}
524524
525const gp_regs = [_]Register{525const gp_regs = [_]Register{
526 .rbx, .r12, .r13, .r14, .r15, .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11,526 .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
527};527};
528const sse_avx_regs = [_]Register{528const sse_avx_regs = [_]Register{
529 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,529 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
src/register_manager.zig+5-3
...@@ -210,13 +210,14 @@ pub fn RegisterManager(...@@ -210,13 +210,14 @@ pub fn RegisterManager(
210 }210 }
211 assert(i == count);211 assert(i == count);
212212
213 for (regs, 0..) |reg, j| {213 for (regs, insts) |reg, inst| {
214 log.debug("tryAllocReg {} for inst {?}", .{ reg, inst });
214 self.markRegAllocated(reg);215 self.markRegAllocated(reg);
215216
216 if (insts[j]) |inst| {217 if (inst) |tracked_inst| {
217 // Track the register218 // Track the register
218 const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null219 const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
219 self.registers[index] = inst;220 self.registers[index] = tracked_inst;
220 self.markRegUsed(reg);221 self.markRegUsed(reg);
221 }222 }
222 }223 }
...@@ -258,6 +259,7 @@ pub fn RegisterManager(...@@ -258,6 +259,7 @@ pub fn RegisterManager(
258 if (excludeRegister(reg, register_class)) break;259 if (excludeRegister(reg, register_class)) break;
259 if (self.isRegLocked(reg)) continue;260 if (self.isRegLocked(reg)) continue;
260261
262 log.debug("allocReg {} for inst {?}", .{ reg, insts[i] });
261 regs[i] = reg;263 regs[i] = reg;
262 self.markRegAllocated(reg);264 self.markRegAllocated(reg);
263 const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null265 const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
test/behavior/array.zig+1
...@@ -191,6 +191,7 @@ test "nested arrays of strings" {...@@ -191,6 +191,7 @@ test "nested arrays of strings" {
191 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;191 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
192 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;192 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
193 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO193 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
194195
195 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };196 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
196 for (array_of_strings, 0..) |s, i| {197 for (array_of_strings, 0..) |s, i| {
test/behavior/bugs/10970.zig-1
...@@ -6,7 +6,6 @@ fn retOpt() ?u32 {...@@ -6,7 +6,6 @@ fn retOpt() ?u32 {
6test "breaking from a loop in an if statement" {6test "breaking from a loop in an if statement" {
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1110
12 var cond = true;11 var cond = true;
test/behavior/cast.zig-5
...@@ -419,7 +419,6 @@ fn testCastIntToErr(err: anyerror) !void {...@@ -419,7 +419,6 @@ fn testCastIntToErr(err: anyerror) !void {
419test "peer resolve array and const slice" {419test "peer resolve array and const slice" {
420 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;420 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
421 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO421 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
422 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
423 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO422 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
424423
425 try testPeerResolveArrayConstSlice(true);424 try testPeerResolveArrayConstSlice(true);
...@@ -818,7 +817,6 @@ test "peer type resolution: error union after non-error" {...@@ -818,7 +817,6 @@ test "peer type resolution: error union after non-error" {
818test "peer cast *[0]T to E![]const T" {817test "peer cast *[0]T to E![]const T" {
819 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;818 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
820 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;819 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
821 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
822 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO820 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
823821
824 var buffer: [5]u8 = "abcde".*;822 var buffer: [5]u8 = "abcde".*;
...@@ -833,7 +831,6 @@ test "peer cast *[0]T to E![]const T" {...@@ -833,7 +831,6 @@ test "peer cast *[0]T to E![]const T" {
833test "peer cast *[0]T to []const T" {831test "peer cast *[0]T to []const T" {
834 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;832 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
835 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;833 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
836 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
837 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO834 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
838835
839 var buffer: [5]u8 = "abcde".*;836 var buffer: [5]u8 = "abcde".*;
...@@ -855,7 +852,6 @@ test "peer cast *[N]T to [*]T" {...@@ -855,7 +852,6 @@ test "peer cast *[N]T to [*]T" {
855test "peer resolution of string literals" {852test "peer resolution of string literals" {
856 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;853 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
857 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO854 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
858 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
859 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO855 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
860856
861 const S = struct {857 const S = struct {
...@@ -1360,7 +1356,6 @@ test "cast f128 to narrower types" {...@@ -1360,7 +1356,6 @@ test "cast f128 to narrower types" {
1360test "peer type resolution: unreachable, null, slice" {1356test "peer type resolution: unreachable, null, slice" {
1361 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1362 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1363 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1364 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1359 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13651360
1366 const S = struct {1361 const S = struct {
test/behavior/for.zig+1
...@@ -275,6 +275,7 @@ test "two counters" {...@@ -275,6 +275,7 @@ test "two counters" {
275test "1-based counter and ptr to array" {275test "1-based counter and ptr to array" {
276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
278279
279 var ok: usize = 0;280 var ok: usize = 0;
280281
test/behavior/if.zig-1
...@@ -112,7 +112,6 @@ test "if prongs cast to expected type instead of peer type resolution" {...@@ -112,7 +112,6 @@ test "if prongs cast to expected type instead of peer type resolution" {
112}112}
113113
114test "if peer expressions inferred optional type" {114test "if peer expressions inferred optional type" {
115 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;115 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
117 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
118 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO117 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/switch.zig-1
...@@ -509,7 +509,6 @@ test "return result loc and then switch with range implicit casted to error unio...@@ -509,7 +509,6 @@ test "return result loc and then switch with range implicit casted to error unio
509}509}
510510
511test "switch with null and T peer types and inferred result location type" {511test "switch with null and T peer types and inferred result location type" {
512 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO512 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
514 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
515 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/union.zig-1
...@@ -1514,7 +1514,6 @@ test "packed union with zero-bit field" {...@@ -1514,7 +1514,6 @@ test "packed union with zero-bit field" {
1514}1514}
15151515
1516test "reinterpreting enum value inside packed union" {1516test "reinterpreting enum value inside packed union" {
1517 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1518 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1517 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1519 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1518 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1520 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1519 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
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)