authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-23 00:27:25-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-24 17:57:58-04:00
logdbe1b4a7e5731e4fb17d42b754faf1052aa78f32
tree2b64d85f1e3ab39bdbc6b7db067501b9b3f38711
parentf99b75360db55413f4accf43a6f4161b14a5de9f

x86_64: fix value tracking bugs


8 files changed, 182 insertions(+), 143 deletions(-)

src/arch/x86_64/CodeGen.zig+174-136
......@@ -265,12 +265,15 @@ pub fn generate(
265265 const fn_type = fn_owner_decl.ty;
266266
267267 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
268 try branch_stack.ensureUnusedCapacity(2);
269 // The outermost branch is used for constants only.
270 branch_stack.appendAssumeCapacity(.{});
271 branch_stack.appendAssumeCapacity(.{});
268272 defer {
269 assert(branch_stack.items.len == 1);
270 branch_stack.items[0].deinit(bin_file.allocator);
273 assert(branch_stack.items.len == 2);
274 for (branch_stack.items) |*branch| branch.deinit(bin_file.allocator);
271275 branch_stack.deinit();
272276 }
273 try branch_stack.append(.{});
274277
275278 var function = Self{
276279 .gpa = bin_file.allocator,
......@@ -1070,20 +1073,29 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10701073 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
10711074 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] });
10721075 }
1076
1077 { // check consistency of tracked registers
1078 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
1079 while (it.next()) |index| {
1080 const tracked_inst = self.register_manager.registers[index];
1081 switch (air_tags[tracked_inst]) {
1082 .block => {},
1083 else => assert(RegisterManager.indexOfRegIntoTracked(
1084 switch (self.getResolvedInstValue(tracked_inst).?) {
1085 .register => |reg| reg,
1086 .register_overflow => |ro| ro.reg,
1087 else => unreachable,
1088 },
1089 ).? == index),
1090 }
1091 }
1092 }
10731093 }
10741094 }
10751095}
10761096
1077/// Asserts there is already capacity to insert into top branch inst_table.
1078fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1079 const air_tags = self.air.instructions.items(.tag);
1080 if (air_tags[inst] == .constant) return; // Constants are immortal.
1081 const prev_value = self.getResolvedInstValue(inst) orelse return;
1082 log.debug("%{d} => {}", .{ inst, MCValue.dead });
1083 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1084 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1085 branch.inst_table.putAssumeCapacity(inst, .dead);
1086 switch (prev_value) {
1097fn freeValue(self: *Self, value: MCValue) void {
1098 switch (value) {
10871099 .register => |reg| {
10881100 self.register_manager.freeReg(reg);
10891101 },
......@@ -1098,6 +1110,18 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
10981110 }
10991111}
11001112
1113/// Asserts there is already capacity to insert into top branch inst_table.
1114fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1115 const air_tags = self.air.instructions.items(.tag);
1116 if (air_tags[inst] == .constant) return; // Constants are immortal.
1117 const prev_value = self.getResolvedInstValue(inst) orelse return;
1118 log.debug("%{d} => {}", .{ inst, MCValue.dead });
1119 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1120 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1121 branch.inst_table.putAssumeCapacity(inst, .dead);
1122 self.freeValue(prev_value);
1123}
1124
11011125/// Called when there are no operands, and the instruction is always unreferenced.
11021126fn finishAirBookkeeping(self: *Self) void {
11031127 if (std.debug.runtime_safety) {
......@@ -1140,13 +1164,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
11401164 },
11411165 else => {},
11421166 }
1167 } else switch (result) {
1168 .none, .dead, .unreach => {},
1169 else => unreachable, // Why didn't the result die?
11431170 }
11441171 self.finishAirBookkeeping();
11451172}
11461173
11471174fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1175 // In addition to the caller's needs, we need enough space to spill every register and eflags.
11481176 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1149 try table.ensureUnusedCapacity(self.gpa, additional_count);
1177 try table.ensureUnusedCapacity(self.gpa, additional_count + self.register_manager.registers.len + 1);
11501178}
11511179
11521180fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
......@@ -1252,12 +1280,15 @@ fn captureState(self: *Self) !State {
12521280 };
12531281}
12541282
1255fn revertState(self: *Self, state: State) void {
1283fn revertState(self: *Self, state: State) !void {
1284 var stack = try state.stack.clone(self.gpa);
1285 errdefer stack.deinit(self.gpa);
1286
12561287 self.register_manager.registers = state.registers;
12571288 self.eflags_inst = state.eflags_inst;
12581289
12591290 self.stack.deinit(self.gpa);
1260 self.stack = state.stack;
1291 self.stack = stack;
12611292
12621293 self.next_stack_offset = state.next_stack_offset;
12631294 self.register_manager.free_registers = state.free_registers;
......@@ -1277,7 +1308,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
12771308 else => {},
12781309 }
12791310 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1280 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1311 branch.inst_table.putAssumeCapacity(inst, stack_mcv);
12811312 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
12821313}
12831314
......@@ -1294,7 +1325,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
12941325 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });
12951326
12961327 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);
1328 branch.inst_table.putAssumeCapacity(inst_to_save, new_mcv);
12981329
12991330 self.eflags_inst = null;
13001331
......@@ -1347,13 +1378,23 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty
13471378}
13481379
13491380fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1350 const stack_offset = try self.allocMemPtr(inst);
1351 return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none });
1381 const result: MCValue = result: {
1382 if (self.liveness.isUnused(inst)) break :result .dead;
1383
1384 const stack_offset = try self.allocMemPtr(inst);
1385 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1386 };
1387 return self.finishAir(inst, result, .{ .none, .none, .none });
13521388}
13531389
13541390fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1355 const stack_offset = try self.allocMemPtr(inst);
1356 return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none });
1391 const result: MCValue = result: {
1392 if (self.liveness.isUnused(inst)) break :result .dead;
1393
1394 const stack_offset = try self.allocMemPtr(inst);
1395 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1396 };
1397 return self.finishAir(inst, result, .{ .none, .none, .none });
13571398}
13581399
13591400fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1992,11 +2033,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
19922033 },
19932034 .register => |reg| {
19942035 // 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
20002036 const eu_lock = self.register_manager.lockReg(reg);
20012037 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20022038
......@@ -2047,11 +2083,6 @@ fn genUnwrapErrorUnionPayloadMir(
20472083 },
20482084 .register => |reg| {
20492085 // 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
20552086 const eu_lock = self.register_manager.lockReg(reg);
20562087 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20572088
......@@ -2877,17 +2908,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
28772908 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
28782909 const imm_0000_0001 = Immediate.u(mask / 0b1111_1111);
28792910
2880 const tmp_reg = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2881 src_mcv.register
2911 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2912 src_mcv
28822913 else
2883 try self.copyToTmpRegister(src_ty, src_mcv);
2884 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2885 defer self.register_manager.unlockReg(tmp_lock);
2886
2887 const dst_reg = try self.register_manager.allocReg(inst, gp);
2914 try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv);
2915 const dst_reg = dst_mcv.register;
28882916 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
28892917 defer self.register_manager.unlockReg(dst_lock);
28902918
2919 const tmp_reg = try self.register_manager.allocReg(null, gp);
2920 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2921 defer self.register_manager.unlockReg(tmp_lock);
2922
28912923 {
28922924 const dst = registerAlias(dst_reg, src_abi_size);
28932925 const tmp = registerAlias(tmp_reg, src_abi_size);
......@@ -2896,9 +2928,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
28962928 else
28972929 undefined;
28982930
2899 // tmp = operand
2900 try self.asmRegisterRegister(.mov, dst, tmp);
29012931 // dst = operand
2932 try self.asmRegisterRegister(.mov, tmp, dst);
2933 // tmp = operand
29022934 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));
29032935 // tmp = operand >> 1
29042936 if (src_abi_size > 4) {
......@@ -2948,7 +2980,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
29482980 }
29492981 // dst = (temp3 * 0x01...01) >> (bits - 8)
29502982 }
2951 break :result .{ .register = dst_reg };
2983 break :result dst_mcv;
29522984 };
29532985 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
29542986}
......@@ -3796,8 +3828,6 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue
37963828
37973829/// Clobbers .rcx for non-immediate shift value.
37983830fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void {
3799 assert(reg.to64() != .rcx);
3800
38013831 switch (tag) {
38023832 .sal, .sar, .shl, .shr => {},
38033833 else => unreachable,
......@@ -4612,23 +4642,24 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
46124642 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
46134643 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
46144644
4615 if (self.liveness.isUnused(inst))
4616 return self.finishAirBookkeeping();
4645 const result: MCValue = result: {
4646 if (self.liveness.isUnused(inst)) break :result .dead;
46174647
4618 const dst_mcv: MCValue = switch (mcv) {
4619 .register => |reg| blk: {
4620 self.register_manager.getRegAssumeFree(reg.to64(), inst);
4621 break :blk MCValue{ .register = reg };
4622 },
4623 .stack_offset => |off| blk: {
4624 const offset = @intCast(i32, self.max_end_stack) - off + 16;
4625 break :blk MCValue{ .stack_offset = -offset };
4626 },
4627 else => return self.fail("TODO implement arg for {}", .{mcv}),
4648 const dst_mcv: MCValue = switch (mcv) {
4649 .register => |reg| blk: {
4650 self.register_manager.getRegAssumeFree(reg.to64(), inst);
4651 break :blk MCValue{ .register = reg };
4652 },
4653 .stack_offset => |off| blk: {
4654 const offset = @intCast(i32, self.max_end_stack) - off + 16;
4655 break :blk MCValue{ .stack_offset = -offset };
4656 },
4657 else => return self.fail("TODO implement arg for {}", .{mcv}),
4658 };
4659 try self.genArgDbgInfo(ty, name, dst_mcv);
4660 break :result dst_mcv;
46284661 };
4629 try self.genArgDbgInfo(ty, name, dst_mcv);
4630
4631 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
4662 return self.finishAir(inst, result, .{ .none, .none, .none });
46324663}
46334664
46344665fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
......@@ -4924,6 +4955,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
49244955 }
49254956
49264957 const result: MCValue = result: {
4958 if (self.liveness.isUnused(inst)) break :result .dead;
4959
49274960 switch (info.return_value) {
49284961 .register => {
49294962 // Save function return value in a new register
......@@ -5137,7 +5170,10 @@ fn genTry(
51375170 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
51385171 try self.genBody(body);
51395172 try self.performReloc(reloc);
5140 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
5173 const result = if (self.liveness.isUnused(inst))
5174 .dead
5175 else
5176 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
51415177 return result;
51425178}
51435179
......@@ -5234,7 +5270,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
52345270 }
52355271
52365272 // Capture the state of register and stack allocation state so that we can revert to it.
5237 const saved_state = try self.captureState();
5273 var saved_state = try self.captureState();
5274 defer saved_state.deinit(self.gpa);
52385275
52395276 {
52405277 try self.branch_stack.append(.{});
......@@ -5252,7 +5289,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
52525289 var then_branch = self.branch_stack.pop();
52535290 defer then_branch.deinit(self.gpa);
52545291
5255 self.revertState(saved_state);
5292 try self.revertState(saved_state);
52565293
52575294 try self.performReloc(reloc);
52585295
......@@ -5286,9 +5323,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
52865323
52875324 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
52885325 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5289
5290 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
5291 try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch);
5326 try self.canonicaliseBranches(true, &then_branch, &else_branch);
52925327
52935328 // We already took care of pl_op.operand earlier, so we're going
52945329 // to pass .none here
......@@ -5423,10 +5458,6 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !
54235458 try self.genBinOpMir(.cmp, Type.anyerror, .{ .stack_offset = offset }, .{ .immediate = 0 });
54245459 },
54255460 .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
54305461 const eu_lock = self.register_manager.lockReg(reg);
54315462 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
54325463
......@@ -5606,7 +5637,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
56065637 // break instruction will choose a MCValue for the block result and overwrite
56075638 // this field. Following break instructions will use that MCValue to put their
56085639 // block results.
5609 .mcv = .none,
5640 .mcv = if (self.liveness.isUnused(inst)) .dead else .none,
56105641 });
56115642 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
56125643
......@@ -5646,21 +5677,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56465677 }
56475678 }
56485679
5649 var branch_stack = std.ArrayList(Branch).init(self.gpa);
5650 defer {
5651 for (branch_stack.items) |*bs| {
5652 bs.deinit(self.gpa);
5653 }
5654 branch_stack.deinit();
5680 log.debug("airSwitch: %{d}", .{inst});
5681 log.debug("Upper branches:", .{});
5682 for (self.branch_stack.items) |bs| {
5683 log.debug("{}", .{bs.fmtDebug()});
56555684 }
5656 try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1);
56575685
5686 var prev_branch: ?Branch = null;
5687 defer if (prev_branch) |*branch| branch.deinit(self.gpa);
5688
5689 // Capture the state of register and stack allocation state so that we can revert to it.
5690 var saved_state = try self.captureState();
5691 defer saved_state.deinit(self.gpa);
5692
5693 const cases_len = switch_br.data.cases_len + @boolToInt(switch_br.data.else_body_len > 0);
56585694 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
56595695 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
56605696 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
56615697 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
56625698 extra_index = case.end + items.len + case_body.len;
56635699
5700 // Revert to the previous register and stack allocation state.
5701 if (prev_branch) |_| try self.revertState(saved_state);
5702
56645703 var relocs = try self.gpa.alloc(u32, items.len);
56655704 defer self.gpa.free(relocs);
56665705
......@@ -5671,12 +5710,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56715710 reloc.* = try self.asmJccReloc(undefined, .ne);
56725711 }
56735712
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
56775713 {
5678 try self.branch_stack.append(.{});
5679 errdefer _ = self.branch_stack.pop();
5714 if (cases_len > 1) try self.branch_stack.append(.{});
5715 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
56805716
56815717 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
56825718 for (liveness.deaths[case_i]) |operand| {
......@@ -5686,25 +5722,31 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56865722 try self.genBody(case_body);
56875723 }
56885724
5689 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5690
5691 // Revert to the previous register and stack allocation state.
5692 self.revertState(saved_state);
5725 // Consolidate returned MCValues between prongs like we do in airCondBr.
5726 if (cases_len > 1) {
5727 var case_branch = self.branch_stack.pop();
5728 errdefer case_branch.deinit(self.gpa);
56935729
5694 for (relocs) |reloc| {
5695 try self.performReloc(reloc);
5730 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });
5731 if (prev_branch) |*canon_branch| {
5732 try self.canonicaliseBranches(case_i == cases_len - 1, canon_branch, &case_branch);
5733 canon_branch.deinit(self.gpa);
5734 }
5735 prev_branch = case_branch;
56965736 }
5737
5738 for (relocs) |reloc| try self.performReloc(reloc);
56975739 }
56985740
56995741 if (switch_br.data.else_body_len > 0) {
57005742 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
57015743
5702 // Capture the state of register and stack allocation state so that we can revert to it.
5703 const saved_state = try self.captureState();
5744 // Revert to the previous register and stack allocation state.
5745 if (prev_branch) |_| try self.revertState(saved_state);
57045746
57055747 {
5706 try self.branch_stack.append(.{});
5707 errdefer _ = self.branch_stack.pop();
5748 if (cases_len > 1) try self.branch_stack.append(.{});
5749 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
57085750
57095751 const else_deaths = liveness.deaths.len - 1;
57105752 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
......@@ -5715,53 +5757,48 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
57155757 try self.genBody(else_body);
57165758 }
57175759
5718 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5760 // Consolidate returned MCValues between a prong and the else branch like we do in airCondBr.
5761 if (cases_len > 1) {
5762 var else_branch = self.branch_stack.pop();
5763 errdefer else_branch.deinit(self.gpa);
57195764
5720 // Revert to the previous register and stack allocation state.
5721 self.revertState(saved_state);
5722 }
5723
5724 // Consolidate returned MCValues between prongs and else branch like we do
5725 // in airCondBr.
5726 log.debug("airSwitch: %{d}", .{inst});
5727 log.debug("Upper branches:", .{});
5728 for (self.branch_stack.items) |bs| {
5729 log.debug("{}", .{bs.fmtDebug()});
5730 }
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);
5765 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5766 if (prev_branch) |*canon_branch| {
5767 try self.canonicaliseBranches(true, canon_branch, &else_branch);
5768 canon_branch.deinit(self.gpa);
5769 }
5770 prev_branch = else_branch;
5771 }
57425772 }
57435773
5744 // We already took care of pl_op.operand earlier, so we're going
5745 // to pass .none here
5774 // We already took care of pl_op.operand earlier, so we're going to pass .none here
57465775 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
57475776}
57485777
5749fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void {
5750 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());
5778fn canonicaliseBranches(
5779 self: *Self,
5780 update_parent: bool,
5781 canon_branch: *Branch,
5782 target_branch: *const Branch,
5783) !void {
5784 const parent_branch =
5785 if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined;
5786 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
57515787
57525788 const target_slice = target_branch.inst_table.entries.slice();
57535789 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {
57545790 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
57555791 // The instruction's MCValue is overridden in both branches.
5756 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5792 if (update_parent) {
5793 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5794 }
57575795 if (target_value == .dead) {
57585796 assert(canon_entry.value == .dead);
57595797 continue;
57605798 }
57615799 break :blk canon_entry.value;
57625800 } else blk: {
5763 if (target_value == .dead)
5764 continue;
5801 if (target_value == .dead) continue;
57655802 // The instruction is only overridden in the else branch.
57665803 // If integer overflows occurs, the question is: why wasn't the instruction marked dead?
57675804 break :blk self.getResolvedInstValue(target_key).?;
......@@ -5770,22 +5807,25 @@ fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Bran
57705807 // TODO make sure the destination stack offset / register does not already have something
57715808 // going on there.
57725809 try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value);
5810 self.freeValue(target_value);
57735811 // TODO track the new register / stack allocation
57745812 }
5775 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());
5813 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());
57765814 const canon_slice = canon_branch.inst_table.entries.slice();
57775815 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {
57785816 // We already deleted the items from this table that matched the target_branch.
57795817 // So these are all instructions that are only overridden in the canon branch.
5780 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
5781 log.debug("canon_value = {}", .{canon_value});
5782 if (canon_value == .dead)
5783 continue;
5784 const parent_mcv = self.getResolvedInstValue(canon_key).?;
5818 const parent_mcv =
5819 if (canon_value != .dead) self.getResolvedInstValue(canon_key).? else undefined;
5820 if (update_parent) {
5821 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
5822 }
5823 if (canon_value == .dead) continue;
57855824 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
57865825 // TODO make sure the destination stack offset / register does not already have something
57875826 // going on there.
5788 try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value);
5827 try self.setRegOrMem(self.air.typeOfIndex(canon_key), canon_value, parent_mcv);
5828 self.freeValue(parent_mcv);
57895829 // TODO track the new register / stack allocation
57905830 }
57915831}
......@@ -5811,11 +5851,9 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
58115851
58125852fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
58135853 const block_data = self.blocks.getPtr(block).?;
5814
5815 if (self.air.typeOf(operand).hasRuntimeBits()) {
5854 if (block_data.mcv != .dead and self.air.typeOf(operand).hasRuntimeBits()) {
58165855 const operand_mcv = try self.resolveInst(operand);
5817 const block_mcv = block_data.mcv;
5818 if (block_mcv == .none) {
5856 if (block_data.mcv == .none) {
58195857 block_data.mcv = switch (operand_mcv) {
58205858 .none, .dead, .unreach => unreachable,
58215859 .register, .stack_offset, .memory => operand_mcv,
......@@ -5827,7 +5865,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
58275865 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
58285866 };
58295867 } else {
5830 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
5868 try self.setRegOrMem(self.air.typeOfIndex(block), block_data.mcv, operand_mcv);
58315869 }
58325870 }
58335871 return self.brVoid(block);
......@@ -6916,7 +6954,8 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
69166954 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
69176955 const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data;
69186956
6919 const dst_reg = try self.register_manager.allocReg(inst, gp);
6957 const unused = self.liveness.isUnused(inst);
6958 const dst_reg = try self.register_manager.allocReg(if (unused) null else inst, gp);
69206959
69216960 const ptr_ty = self.air.typeOf(pl_op.operand);
69226961 const ptr_mcv = try self.resolveInst(pl_op.operand);
......@@ -6924,7 +6963,6 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
69246963 const val_ty = self.air.typeOf(extra.operand);
69256964 const val_mcv = try self.resolveInst(extra.operand);
69266965
6927 const unused = self.liveness.isUnused(inst);
69286966 try self.atomicOp(dst_reg, ptr_mcv, val_mcv, ptr_ty, val_ty, unused, extra.op(), extra.ordering());
69296967 const result: MCValue = if (unused) .dead else .{ .register = dst_reg };
69306968 return self.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none });
src/arch/x86_64/abi.zig+1-1
......@@ -523,7 +523,7 @@ pub fn getCAbiIntReturnRegs(target: Target) []const Register {
523523}
524524
525525const 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,
527527};
528528const sse_avx_regs = [_]Register{
529529 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
src/register_manager.zig+5-3
......@@ -210,13 +210,14 @@ pub fn RegisterManager(
210210 }
211211 assert(i == count);
212212
213 for (regs, 0..) |reg, j| {
213 for (regs, insts) |reg, inst| {
214 log.debug("tryAllocReg {} for inst {?}", .{ reg, inst });
214215 self.markRegAllocated(reg);
215216
216 if (insts[j]) |inst| {
217 if (inst) |tracked_inst| {
217218 // Track the register
218219 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;
220221 self.markRegUsed(reg);
221222 }
222223 }
......@@ -258,6 +259,7 @@ pub fn RegisterManager(
258259 if (excludeRegister(reg, register_class)) break;
259260 if (self.isRegLocked(reg)) continue;
260261
262 log.debug("allocReg {} for inst {?}", .{ reg, insts[i] });
261263 regs[i] = reg;
262264 self.markRegAllocated(reg);
263265 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" {
191191 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
192192 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
193193 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
194195
195196 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
196197 for (array_of_strings, 0..) |s, i| {
test/behavior/bugs/10970.zig-1
......@@ -6,7 +6,6 @@ fn retOpt() ?u32 {
66test "breaking from a loop in an if statement" {
77 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1110
1211 var cond = true;
test/behavior/for.zig+1
......@@ -275,6 +275,7 @@ test "two counters" {
275275test "1-based counter and ptr to array" {
276276 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
277277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
278279
279280 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" {
112112}
113113
114114test "if peer expressions inferred optional type" {
115 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
116115 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
117116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
118117 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" {
15141514}
15151515
15161516test "reinterpreting enum value inside packed union" {
1517 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15181517 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15191518 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15201519 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO