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 {
213213};
214214
215215const BlockData = struct {
216 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
217 /// The first break instruction encounters `null` here and chooses a
218 /// machine code value for the block result, populating this field.
219 /// Following break instructions encounter that value and use it for
220 /// the location to store their block results.
221 mcv: MCValue,
216 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
217 branch: Branch = .{},
218 branch_depth: u32,
219
220 fn deinit(self: *BlockData, gpa: Allocator) void {
221 self.branch.deinit(gpa);
222 self.relocs.deinit(gpa);
223 self.* = undefined;
224 }
222225};
223226
224227const BigTomb = struct {
......@@ -265,12 +268,15 @@ pub fn generate(
265268 const fn_type = fn_owner_decl.ty;
266269
267270 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(.{});
268275 defer {
269 assert(branch_stack.items.len == 1);
270 branch_stack.items[0].deinit(bin_file.allocator);
276 assert(branch_stack.items.len == 2);
277 for (branch_stack.items) |*branch| branch.deinit(bin_file.allocator);
271278 branch_stack.deinit();
272279 }
273 try branch_stack.append(.{});
274280
275281 var function = Self{
276282 .gpa = bin_file.allocator,
......@@ -1070,20 +1076,36 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10701076 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
10711077 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] });
10721078 }
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 }
10731092 }
10741093 }
10751094}
10761095
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) {
1096fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
1097 const reg = switch (value) {
1098 .register => |reg| reg,
1099 .register_overflow => |ro| ro.reg,
1100 else => return,
1101 };
1102 if (self.register_manager.isRegFree(reg)) {
1103 self.register_manager.getRegAssumeFree(reg, inst);
1104 }
1105}
1106
1107fn freeValue(self: *Self, value: MCValue) void {
1108 switch (value) {
10871109 .register => |reg| {
10881110 self.register_manager.freeReg(reg);
10891111 },
......@@ -1098,6 +1120,18 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
10981120 }
10991121}
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
11011135/// Called when there are no operands, and the instruction is always unreferenced.
11021136fn finishAirBookkeeping(self: *Self) void {
11031137 if (std.debug.runtime_safety) {
......@@ -1121,32 +1155,21 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
11211155 log.debug("%{d} => {}", .{ inst, result });
11221156 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
11231157 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
1124
1125 // In some cases (such as bitcast), an operand
1126 // may be the same MCValue as the result. If
1127 // that operand died and was a register, it
1128 // was freed by processDeath. We have to
1129 // "re-allocate" the register.
1130 switch (result) {
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 }
1158 // In some cases, an operand may be reused as the result.
1159 // If that operand died and was a register, it was freed by
1160 // processDeath, so we have to "re-allocate" the register.
1161 self.getValue(result, inst);
1162 } else switch (result) {
1163 .none, .dead, .unreach => {},
1164 else => unreachable, // Why didn't the result die?
11431165 }
11441166 self.finishAirBookkeeping();
11451167}
11461168
11471169fn 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.
11481171 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);
11501173}
11511174
11521175fn 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_
12311254}
12321255
12331256const State = struct {
1234 next_stack_offset: u32,
12351257 registers: abi.RegisterManager.TrackedRegisters,
12361258 free_registers: abi.RegisterManager.RegisterBitSet,
12371259 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 }
12431260};
12441261
1245fn captureState(self: *Self) !State {
1262fn captureState(self: *Self) State {
12461263 return State{
1247 .next_stack_offset = self.next_stack_offset,
12481264 .registers = self.register_manager.registers,
12491265 .free_registers = self.register_manager.free_registers,
12501266 .eflags_inst = self.eflags_inst,
1251 .stack = try self.stack.clone(self.gpa),
12521267 };
12531268}
12541269
12551270fn revertState(self: *Self, state: State) void {
1256 self.register_manager.registers = state.registers;
12571271 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;
12631272 self.register_manager.free_registers = state.free_registers;
1273 self.register_manager.registers = state.registers;
12641274}
12651275
12661276pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
12671277 const stack_mcv = try self.allocRegOrMem(inst, false);
12681278 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).?.*;
12701280 switch (reg_mcv) {
12711281 .register => |other| {
12721282 assert(reg.to64() == other.to64());
......@@ -1277,13 +1287,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
12771287 else => {},
12781288 }
12791289 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);
12811291 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
12821292}
12831293
12841294pub fn spillEflagsIfOccupied(self: *Self) !void {
12851295 if (self.eflags_inst) |inst_to_save| {
1286 const mcv = self.getResolvedInstValue(inst_to_save).?;
1296 const mcv = self.getResolvedInstValue(inst_to_save).?.*;
12871297 const new_mcv = switch (mcv) {
12881298 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
12891299 .eflags => try self.allocRegOrMem(inst_to_save, true),
......@@ -1294,7 +1304,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
12941304 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });
12951305
12961306 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
12991309 self.eflags_inst = null;
13001310
......@@ -1347,13 +1357,23 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty
13471357}
13481358
13491359fn 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 });
1360 const result: MCValue = result: {
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 });
13521367}
13531368
13541369fn 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 });
1370 const result: MCValue = result: {
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 });
13571377}
13581378
13591379fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1992,11 +2012,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
19922012 },
19932013 .register => |reg| {
19942014 // 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
20002015 const eu_lock = self.register_manager.lockReg(reg);
20012016 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20022017
......@@ -2047,11 +2062,6 @@ fn genUnwrapErrorUnionPayloadMir(
20472062 },
20482063 .register => |reg| {
20492064 // 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
20552065 const eu_lock = self.register_manager.lockReg(reg);
20562066 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
20572067
......@@ -2749,7 +2759,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
27492759 };
27502760 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);
27532763 const dst_mcv = MCValue{ .register = dst_reg };
27542764 const dst_lock = self.register_manager.lockReg(dst_reg);
27552765 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -2764,14 +2774,14 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
27642774 }
27652775
27662776 const src_bits = src_ty.bitSize(self.target.*);
2767 const width_reg = try self.copyToTmpRegister(dst_ty, .{ .immediate = src_bits });
2768 const width_mcv = MCValue{ .register = width_reg };
2777 const width_mcv =
2778 try self.copyToRegisterWithInstTracking(inst, dst_ty, .{ .immediate = src_bits });
27692779 try self.genBinOpMir(.bsr, src_ty, dst_mcv, mat_src_mcv);
27702780
27712781 const dst_abi_size = @intCast(u32, @max(dst_ty.abiSize(self.target.*), 2));
27722782 try self.asmCmovccRegisterRegister(
27732783 registerAlias(dst_reg, dst_abi_size),
2774 registerAlias(width_reg, dst_abi_size),
2784 registerAlias(width_mcv.register, dst_abi_size),
27752785 .z,
27762786 );
27772787
......@@ -2835,7 +2845,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
28352845 registerAlias(width_reg, abi_size),
28362846 .z,
28372847 );
2838
28392848 break :result dst_mcv;
28402849 };
28412850 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -2877,17 +2886,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
28772886 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
28782887 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))
2881 src_mcv.register
2889 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2890 src_mcv
28822891 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);
2892 try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv);
2893 const dst_reg = dst_mcv.register;
28882894 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
28892895 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
28912901 {
28922902 const dst = registerAlias(dst_reg, src_abi_size);
28932903 const tmp = registerAlias(tmp_reg, src_abi_size);
......@@ -2896,9 +2906,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
28962906 else
28972907 undefined;
28982908
2899 // tmp = operand
2900 try self.asmRegisterRegister(.mov, dst, tmp);
29012909 // dst = operand
2910 try self.asmRegisterRegister(.mov, tmp, dst);
2911 // tmp = operand
29022912 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));
29032913 // tmp = operand >> 1
29042914 if (src_abi_size > 4) {
......@@ -2948,7 +2958,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
29482958 }
29492959 // dst = (temp3 * 0x01...01) >> (bits - 8)
29502960 }
2951 break :result .{ .register = dst_reg };
2961 break :result dst_mcv;
29522962 };
29532963 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
29542964}
......@@ -3170,8 +3180,8 @@ fn reuseOperand(
31703180 .register => |reg| {
31713181 // If it's in the registers table, need to associate the register with the
31723182 // new instruction.
3173 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
3174 if (!self.register_manager.isRegFree(reg)) {
3183 if (!self.register_manager.isRegFree(reg)) {
3184 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
31753185 self.register_manager.registers[index] = inst;
31763186 }
31773187 }
......@@ -3510,7 +3520,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
35103520 const value_ty = self.air.typeOf(bin_op.rhs);
35113521 log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value });
35123522 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 });
35143524}
35153525
35163526fn 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
37963806
37973807/// Clobbers .rcx for non-immediate shift value.
37983808fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void {
3799 assert(reg.to64() != .rcx);
3800
38013809 switch (tag) {
38023810 .sal, .sar, .shl, .shr => {},
38033811 else => unreachable,
......@@ -4612,23 +4620,24 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
46124620 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
46134621 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
46144622
4615 if (self.liveness.isUnused(inst))
4616 return self.finishAirBookkeeping();
4623 const result: MCValue = result: {
4624 if (self.liveness.isUnused(inst)) break :result .dead;
46174625
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}),
4626 const dst_mcv: MCValue = switch (mcv) {
4627 .register => |reg| blk: {
4628 self.register_manager.getRegAssumeFree(reg.to64(), inst);
4629 break :blk MCValue{ .register = reg };
4630 },
4631 .stack_offset => |off| blk: {
4632 const offset = @intCast(i32, self.max_end_stack) - off + 16;
4633 break :blk MCValue{ .stack_offset = -offset };
4634 },
4635 else => return self.fail("TODO implement arg for {}", .{mcv}),
4636 };
4637 try self.genArgDbgInfo(ty, name, dst_mcv);
4638 break :result dst_mcv;
46284639 };
4629 try self.genArgDbgInfo(ty, name, dst_mcv);
4630
4631 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
4640 return self.finishAir(inst, result, .{ .none, .none, .none });
46324641}
46334642
46344643fn 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
49244933 }
49254934
49264935 const result: MCValue = result: {
4936 if (self.liveness.isUnused(inst)) break :result .dead;
4937
49274938 switch (info.return_value) {
49284939 .register => {
49294940 // Save function return value in a new register
......@@ -5137,7 +5148,10 @@ fn genTry(
51375148 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
51385149 try self.genBody(body);
51395150 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);
51415155 return result;
51425156}
51435157
......@@ -5226,15 +5240,11 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
52265240 // that death now instead of later as this has an effect on
52275241 // whether it needs to be spilled in the branches
52285242 if (self.liveness.operandDies(inst, 0)) {
5229 const op_int = @enumToInt(pl_op.operand);
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 }
5243 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
52345244 }
52355245
52365246 // 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
52395249 {
52405250 try self.branch_stack.append(.{});
......@@ -5283,12 +5293,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
52835293 for (self.branch_stack.items) |bs| {
52845294 log.debug("{}", .{bs.fmtDebug()});
52855295 }
5286
52875296 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
52885297 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
52895298
5290 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
5291 try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch);
5299 try self.canonicaliseBranches(true, &then_branch, &else_branch, true, true);
52925300
52935301 // We already took care of pl_op.operand earlier, so we're going
52945302 // to pass .none here
......@@ -5423,10 +5431,6 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !
54235431 try self.genBinOpMir(.cmp, Type.anyerror, .{ .stack_offset = offset }, .{ .immediate = 0 });
54245432 },
54255433 .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
54305434 const eu_lock = self.register_manager.lockReg(reg);
54315435 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock);
54325436
......@@ -5598,27 +5602,46 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
55985602}
55995603
56005604fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5601 try self.blocks.putNoClobber(self.gpa, inst, .{
5602 // A block is a setup to be able to jump to the end.
5603 .relocs = .{},
5604 // It also acts as a receptacle for break operands.
5605 // Here we use `MCValue.none` to represent a null value so that the first
5606 // break instruction will choose a MCValue for the block result and overwrite
5607 // this field. Following break instructions will use that MCValue to put their
5608 // block results.
5609 .mcv = .none,
5610 });
5611 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
5605 // A block is a setup to be able to jump to the end.
5606 const branch_depth = @intCast(u32, self.branch_stack.items.len);
5607 try self.blocks.putNoClobber(self.gpa, inst, .{ .branch_depth = branch_depth });
5608 defer {
5609 var block_data = self.blocks.fetchRemove(inst).?.value;
5610 block_data.deinit(self.gpa);
5611 }
56125612
5613 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5614 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5615 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5616 try self.genBody(body);
5613 const ty = self.air.typeOfIndex(inst);
5614 const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst);
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;
5621 return self.finishAir(inst, result, .{ .none, .none, .none });
5626 {
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();
56225645}
56235646
56245647fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5639,28 +5662,31 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56395662 // that death now instead of later as this has an effect on
56405663 // whether it needs to be spilled in the branches
56415664 if (self.liveness.operandDies(inst, 0)) {
5642 const op_int = @enumToInt(pl_op.operand);
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 }
5665 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
56475666 }
56485667
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();
5668 log.debug("airSwitch: %{d}", .{inst});
5669 log.debug("Upper branches:", .{});
5670 for (self.branch_stack.items) |bs| {
5671 log.debug("{}", .{bs.fmtDebug()});
56555672 }
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);
56585681 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
56595682 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
56605683 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
56615684 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
56625685 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
56645690 var relocs = try self.gpa.alloc(u32, items.len);
56655691 defer self.gpa.free(relocs);
56665692
......@@ -5671,12 +5697,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56715697 reloc.* = try self.asmJccReloc(undefined, .ne);
56725698 }
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
56775700 {
5678 try self.branch_stack.append(.{});
5679 errdefer _ = self.branch_stack.pop();
5701 if (cases_len > 1) try self.branch_stack.append(.{});
5702 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
56805703
56815704 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
56825705 for (liveness.deaths[case_i]) |operand| {
......@@ -5686,25 +5709,32 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
56865709 try self.genBody(case_body);
56875710 }
56885711
5689 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5690
5691 // Revert to the previous register and stack allocation state.
5692 self.revertState(saved_state);
5712 // Consolidate returned MCValues between prongs like we do in airCondBr.
5713 if (cases_len > 1) {
5714 var case_branch = self.branch_stack.pop();
5715 errdefer case_branch.deinit(self.gpa);
56935716
5694 for (relocs) |reloc| {
5695 try self.performReloc(reloc);
5717 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });
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;
56965724 }
5725
5726 for (relocs) |reloc| try self.performReloc(reloc);
56975727 }
56985728
56995729 if (switch_br.data.else_body_len > 0) {
57005730 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.
5703 const saved_state = try self.captureState();
5732 // Revert to the previous register and stack allocation state.
5733 if (prev_branch) |_| self.revertState(saved_state);
57045734
57055735 {
5706 try self.branch_stack.append(.{});
5707 errdefer _ = self.branch_stack.pop();
5736 if (cases_len > 1) try self.branch_stack.append(.{});
5737 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
57085738
57095739 const else_deaths = liveness.deaths.len - 1;
57105740 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
......@@ -5715,78 +5745,103 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
57155745 try self.genBody(else_body);
57165746 }
57175747
5718 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5719
5720 // Revert to the previous register and stack allocation state.
5721 self.revertState(saved_state);
5722 }
5748 // Consolidate returned MCValues between a prong and the else branch like we do in airCondBr.
5749 if (cases_len > 1) {
5750 var else_branch = self.branch_stack.pop();
5751 errdefer else_branch.deinit(self.gpa);
57235752
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);
5753 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
5754 if (prev_branch) |*canon_branch| {
5755 try self.canonicaliseBranches(true, canon_branch, &else_branch, true, true);
5756 canon_branch.deinit(self.gpa);
5757 }
5758 prev_branch = else_branch;
5759 }
57425760 }
57435761
5744 // We already took care of pl_op.operand earlier, so we're going
5745 // to pass .none here
5762 // We already took care of pl_op.operand earlier, so we're going to pass .none here
57465763 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
57475764}
57485765
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());
5766fn canonicaliseBranches(
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();
5753 for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| {
5780 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
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.*;
57545785 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
57555786 // The instruction's MCValue is overridden in both branches.
5756 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
57575787 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);
57595792 continue;
57605793 }
5794 if (update_parent) {
5795 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5796 }
57615797 break :blk canon_entry.value;
57625798 } 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 }
57645803 continue;
5804 }
57655805 // The instruction is only overridden in the else branch.
5766 // If integer overflows occurs, the question is: why wasn't the instruction marked dead?
5767 break :blk self.getResolvedInstValue(target_key).?;
5806 // If integer overflow occurs, the question is: why wasn't the instruction marked dead?
5807 break :blk self.getResolvedInstValue(target_key).?.*;
57685808 };
57695809 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 something
5810 // TODO handle the case where the destination stack offset / register has something
57715811 // 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);
57735818 // TODO track the new register / stack allocation
57745819 }
5775 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());
5776 const canon_slice = canon_branch.inst_table.entries.slice();
5777 for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| {
5820
5821 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());
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.*;
57785826 // We already deleted the items from this table that matched the target_branch.
57795827 // 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).?;
5785 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
5786 // TODO make sure the destination stack offset / register does not already have something
5787 // going on there.
5788 try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value);
5789 // TODO track the new register / stack allocation
5828 const parent_mcv =
5829 if (canon_value != .dead) self.getResolvedInstValue(canon_key).?.* else undefined;
5830 if (canon_value != .dead) {
5831 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
5832 // TODO handle the case where the destination stack offset / register has something
5833 // going on there.
5834 assert(!hazard_map.contains(parent_mcv));
5835 try hazard_map.putNoClobber(canon_value, {});
5836 if (set_values) {
5837 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 }
57905845 }
57915846}
57925847
......@@ -5804,42 +5859,79 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
58045859}
58055860
58065861fn airBr(self: *Self, inst: Air.Inst.Index) !void {
5807 const branch = self.air.instructions.items(.data)[inst].br;
5808 try self.br(branch.block_inst, branch.operand);
5809 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
5810}
5862 const br = self.air.instructions.items(.data)[inst].br;
5863 const block = br.block_inst;
5864
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: {
58235876 const new_mcv = try self.allocRegOrMem(block, true);
5824 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
5825 break :blk new_mcv;
5826 },
5827 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
5828 };
5829 } else {
5830 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
5877 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);
5878 break :result new_mcv;
5879 };
5880 dst_mcv.* = result;
5881 self.freeValue(result);
5882 },
5883 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),
58315884 }
58325885 }
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
58375892 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
58385928 // Emit a jump with a relocation. It will be patched up after the block ends.
58395929 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
58405930 // Leave the jump offset undefined
58415931 const jmp_reloc = try self.asmJmpReloc(undefined);
58425932 block_data.relocs.appendAssumeCapacity(jmp_reloc);
5933
5934 self.finishAirBookkeeping();
58435935}
58445936
58455937fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
......@@ -6916,7 +7008,8 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
69167008 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
69177009 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
69217014 const ptr_ty = self.air.typeOf(pl_op.operand);
69227015 const ptr_mcv = try self.resolveInst(pl_op.operand);
......@@ -6924,7 +7017,6 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
69247017 const val_ty = self.air.typeOf(extra.operand);
69257018 const val_mcv = try self.resolveInst(extra.operand);
69267019
6927 const unused = self.liveness.isUnused(inst);
69287020 try self.atomicOp(dst_reg, ptr_mcv, val_mcv, ptr_ty, val_ty, unused, extra.op(), extra.ordering());
69297021 const result: MCValue = if (unused) .dead else .{ .register = dst_reg };
69307022 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 {
72057297 return gop.value_ptr.*;
72067298 },
72077299 .const_ty => unreachable,
7208 else => return self.getResolvedInstValue(inst_index).?,
7300 else => return self.getResolvedInstValue(inst_index).?.*,
72097301 }
72107302}
72117303
7212fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue {
7304fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?*MCValue {
72137305 // Treat each stack item as a "layer" on top of the previous one.
72147306 var i: usize = self.branch_stack.items.len;
72157307 while (true) {
72167308 i -= 1;
7217 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
7218 return if (mcv != .dead) mcv else null;
7309 if (self.branch_stack.items[i].inst_table.getPtr(inst)) |mcv| {
7310 return if (mcv.* != .dead) mcv else null;
72197311 }
72207312 }
72217313}
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/cast.zig-5
......@@ -419,7 +419,6 @@ fn testCastIntToErr(err: anyerror) !void {
419419test "peer resolve array and const slice" {
420420 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
421421 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
422 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
423422 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
424423
425424 try testPeerResolveArrayConstSlice(true);
......@@ -818,7 +817,6 @@ test "peer type resolution: error union after non-error" {
818817test "peer cast *[0]T to E![]const T" {
819818 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
820819 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
821 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
822820 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
823821
824822 var buffer: [5]u8 = "abcde".*;
......@@ -833,7 +831,6 @@ test "peer cast *[0]T to E![]const T" {
833831test "peer cast *[0]T to []const T" {
834832 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
835833 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
836 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
837834 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
838835
839836 var buffer: [5]u8 = "abcde".*;
......@@ -855,7 +852,6 @@ test "peer cast *[N]T to [*]T" {
855852test "peer resolution of string literals" {
856853 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
857854 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
858 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
859855 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
860856
861857 const S = struct {
......@@ -1360,7 +1356,6 @@ test "cast f128 to narrower types" {
13601356test "peer type resolution: unreachable, null, slice" {
13611357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13621358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1363 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13641359 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13651360
13661361 const S = struct {
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/switch.zig-1
......@@ -509,7 +509,6 @@ test "return result loc and then switch with range implicit casted to error unio
509509}
510510
511511test "switch with null and T peer types and inferred result location type" {
512 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
513512 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
514513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
515514 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
tools/lldb_pretty_printers.py+15
......@@ -164,6 +164,20 @@ class zig_ErrorUnion_SynthProvider:
164164 def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1
165165 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
167181# Define Zig Standard Library
168182
169183class std_SegmentedList_SynthProvider:
......@@ -606,3 +620,4 @@ def __lldb_init_module(debugger, _=None):
606620 add(debugger, category='zig.stage2', type='type.Type', summary=True)
607621 add(debugger, category='zig.stage2', type='value.Value', identifier='TagOrPayloadPtr', synth=True)
608622 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)