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 {
2659626596 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
2659726597 }
2659826598
26599 // Ensure a register is available for dispatch.
26600 if (!mat_cond.isRegister()) _ = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
26601
2659926602 self.scope_generation += 1;
2660026603 const state = try self.saveState();
2660126604
......@@ -26618,47 +26621,63 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
2661826621
2661926622 const block_ty = self.typeOfIndex(br.block_inst);
2662026623 const loop_data = self.loops.getPtr(br.block_inst).?;
26621 if (self.loop_switches.getPtr(br.block_inst)) |table| {
26622 // Process operand death so that it is properly accounted for in the State below.
26623 const condition_dies = self.liveness.operandDies(inst, 0);
26624
26625 try self.restoreState(loop_data.state, &.{}, .{
26626 .emit_instructions = true,
26627 .update_tracking = false,
26628 .resurrect = false,
26629 .close_scope = false,
26630 });
26624 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
26625 {
26626 try self.getValue(block_tracking.short, null);
26627 const src_mcv = try self.resolveInst(br.operand);
26628
26629 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) {
26630 try self.getValue(block_tracking.short, br.block_inst);
26631 // .long = .none to avoid merging operand and block result stack frames.
26632 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv };
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| {
2663226654 const condition_ty = self.typeOf(br.operand);
26633 const condition = try self.resolveInst(br.operand);
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 };
26655 const condition_mcv = block_tracking.short;
2663926656 try self.spillEflagsIfOccupied();
2664026657 if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir(
2664126658 .{ ._, .sub },
2664226659 condition_ty,
26643 condition_index,
26660 condition_mcv,
2664426661 .{ .air_ref = Air.internedToRef(table.min.toIntern()) },
2664526662 );
2664626663 switch (table.else_relocs) {
2664726664 .@"unreachable" => {},
2664826665 .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 });
2665026667 try else_relocs.append(self.gpa, try self.asmJccReloc(.a, undefined));
2665126668 },
2665226669 .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 });
2665426671 _ = try self.asmJccReloc(.a, else_reloc);
2665526672 },
2665626673 }
2665726674 {
26658 const condition_index_reg = if (condition_index.isRegister())
26659 condition_index.getReg().?
26660 else
26661 try self.copyToTmpRegister(.usize, condition_index);
26675 const condition_index_reg = if (condition_mcv.isRegister()) condition_mcv.getReg().? else cond: {
26676 const condition_index_reg =
26677 RegisterManager.regAtTrackedIndex(@intCast(loop_data.state.free_registers.findFirstSet().?));
26678 try self.genSetReg(condition_index_reg, condition_ty, condition_mcv, .{});
26679 break :cond condition_index_reg;
26680 };
2666226681 const condition_index_lock = self.register_manager.lockReg(condition_index_reg);
2666326682 defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock);
2666426683 try self.truncateRegister(condition_ty, condition_index_reg);
......@@ -26677,38 +26696,6 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
2667726696 return self.finishAir(inst, .none, .{ br.operand, .none, .none });
2667826697 }
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
2671226699 // Emit a jump with a relocation. It will be patched up after the block ends.
2671326700 // Leave the jump offset undefined
2671426701 _ = try self.asmJmpReloc(loop_data.target);