authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-23 08:07:37-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-24 20:56:11-05:00
log7701cfa032a0d3611310426d5a5027d8862d49b1
treedb7f8677c894e9d7180552d1d33e73f9378cf978
parentba82d6e83e3e0dc00ad235fae52c21f9014ebd78

x86_64: mitigate miscomp during switch dispatch


1 files changed, 42 insertions(+), 55 deletions(-)

src/arch/x86_64/CodeGen.zig+42-55
...@@ -26596,6 +26596,9 @@ fn airLoopSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -26596,6 +26596,9 @@ fn airLoopSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void {
26596 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);26596 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
26597 }26597 }
2659826598
26599 // Ensure a register is available for dispatch.
26600 if (!mat_cond.isRegister()) _ = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
26601
26599 self.scope_generation += 1;26602 self.scope_generation += 1;
26600 const state = try self.saveState();26603 const state = try self.saveState();
2660126604
...@@ -26618,47 +26621,63 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -26618,47 +26621,63 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
2661826621
26619 const block_ty = self.typeOfIndex(br.block_inst);26622 const block_ty = self.typeOfIndex(br.block_inst);
26620 const loop_data = self.loops.getPtr(br.block_inst).?;26623 const loop_data = self.loops.getPtr(br.block_inst).?;
26621 if (self.loop_switches.getPtr(br.block_inst)) |table| {26624 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
26622 // Process operand death so that it is properly accounted for in the State below.26625 {
26623 const condition_dies = self.liveness.operandDies(inst, 0);26626 try self.getValue(block_tracking.short, null);
2662426627 const src_mcv = try self.resolveInst(br.operand);
26625 try self.restoreState(loop_data.state, &.{}, .{26628
26626 .emit_instructions = true,26629 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) {
26627 .update_tracking = false,26630 try self.getValue(block_tracking.short, br.block_inst);
26628 .resurrect = false,26631 // .long = .none to avoid merging operand and block result stack frames.
26629 .close_scope = false,26632 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv };
26630 });26633 try current_tracking.materializeUnsafe(self, br.block_inst, block_tracking.*);
26634 for (current_tracking.getRegs()) |src_reg| self.register_manager.freeReg(src_reg);
26635 } else {
26636 try self.getValue(block_tracking.short, br.block_inst);
26637 try self.genCopy(block_ty, block_tracking.short, try self.resolveInst(br.operand), .{});
26638 }
26639 }
2663126640
26641 // Process operand death so that it is properly accounted for in the State below.
26642 if (self.liveness.operandDies(inst, 0)) {
26643 if (br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
26644 }
26645
26646 try self.restoreState(loop_data.state, &.{}, .{
26647 .emit_instructions = true,
26648 .update_tracking = false,
26649 .resurrect = false,
26650 .close_scope = false,
26651 });
26652
26653 if (self.loop_switches.getPtr(br.block_inst)) |table| {
26632 const condition_ty = self.typeOf(br.operand);26654 const condition_ty = self.typeOf(br.operand);
26633 const condition = try self.resolveInst(br.operand);26655 const condition_mcv = block_tracking.short;
26634 const condition_index = if (condition_dies and condition.isModifiable()) condition else condition_index: {
26635 const condition_index = try self.allocTempRegOrMem(condition_ty, true);
26636 try self.genCopy(condition_ty, condition_index, condition, .{});
26637 break :condition_index condition_index;
26638 };
26639 try self.spillEflagsIfOccupied();26656 try self.spillEflagsIfOccupied();
26640 if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir(26657 if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir(
26641 .{ ._, .sub },26658 .{ ._, .sub },
26642 condition_ty,26659 condition_ty,
26643 condition_index,26660 condition_mcv,
26644 .{ .air_ref = Air.internedToRef(table.min.toIntern()) },26661 .{ .air_ref = Air.internedToRef(table.min.toIntern()) },
26645 );26662 );
26646 switch (table.else_relocs) {26663 switch (table.else_relocs) {
26647 .@"unreachable" => {},26664 .@"unreachable" => {},
26648 .forward => |*else_relocs| {26665 .forward => |*else_relocs| {
26649 try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_index, .{ .immediate = table.len - 1 });26666 try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_mcv, .{ .immediate = table.len - 1 });
26650 try else_relocs.append(self.gpa, try self.asmJccReloc(.a, undefined));26667 try else_relocs.append(self.gpa, try self.asmJccReloc(.a, undefined));
26651 },26668 },
26652 .backward => |else_reloc| {26669 .backward => |else_reloc| {
26653 try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_index, .{ .immediate = table.len - 1 });26670 try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition_mcv, .{ .immediate = table.len - 1 });
26654 _ = try self.asmJccReloc(.a, else_reloc);26671 _ = try self.asmJccReloc(.a, else_reloc);
26655 },26672 },
26656 }26673 }
26657 {26674 {
26658 const condition_index_reg = if (condition_index.isRegister())26675 const condition_index_reg = if (condition_mcv.isRegister()) condition_mcv.getReg().? else cond: {
26659 condition_index.getReg().?26676 const condition_index_reg =
26660 else26677 RegisterManager.regAtTrackedIndex(@intCast(loop_data.state.free_registers.findFirstSet().?));
26661 try self.copyToTmpRegister(.usize, condition_index);26678 try self.genSetReg(condition_index_reg, condition_ty, condition_mcv, .{});
26679 break :cond condition_index_reg;
26680 };
26662 const condition_index_lock = self.register_manager.lockReg(condition_index_reg);26681 const condition_index_lock = self.register_manager.lockReg(condition_index_reg);
26663 defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock);26682 defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock);
26664 try self.truncateRegister(condition_ty, condition_index_reg);26683 try self.truncateRegister(condition_ty, condition_index_reg);
...@@ -26677,38 +26696,6 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -26677,38 +26696,6 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
26677 return self.finishAir(inst, .none, .{ br.operand, .none, .none });26696 return self.finishAir(inst, .none, .{ br.operand, .none, .none });
26678 }26697 }
2667926698
26680 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
26681 done: {
26682 try self.getValue(block_tracking.short, null);
26683 const src_mcv = try self.resolveInst(br.operand);
26684
26685 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) {
26686 try self.getValue(block_tracking.short, br.block_inst);
26687 // .long = .none to avoid merging operand and block result stack frames.
26688 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv };
26689 try current_tracking.materializeUnsafe(self, br.block_inst, block_tracking.*);
26690 for (current_tracking.getRegs()) |src_reg| self.register_manager.freeReg(src_reg);
26691 break :done;
26692 }
26693
26694 try self.getValue(block_tracking.short, br.block_inst);
26695 const dst_mcv = block_tracking.short;
26696 try self.genCopy(block_ty, dst_mcv, try self.resolveInst(br.operand), .{});
26697 break :done;
26698 }
26699
26700 // Process operand death so that it is properly accounted for in the State below.
26701 if (self.liveness.operandDies(inst, 0)) {
26702 if (br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
26703 }
26704
26705 try self.restoreState(loop_data.state, &.{}, .{
26706 .emit_instructions = true,
26707 .update_tracking = false,
26708 .resurrect = false,
26709 .close_scope = false,
26710 });
26711
26712 // Emit a jump with a relocation. It will be patched up after the block ends.26699 // Emit a jump with a relocation. It will be patched up after the block ends.
26713 // Leave the jump offset undefined26700 // Leave the jump offset undefined
26714 _ = try self.asmJmpReloc(loop_data.target);26701 _ = try self.asmJmpReloc(loop_data.target);