| author | |
| committer | |
| log | edd63f9abaa7dae1786a4cd91cc3031264bd3ef0 |
| tree | 2259ac328fe3d045d0a5ce318b55b611cff7da81 |
| parent | c865c8fb2a910e73e38192a8bd484935bae8f6fc |
7 files changed, 270 insertions(+), 215 deletions(-)
src/arch/x86_64/CodeGen.zig+88-137| ... | @@ -1286,7 +1286,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { | ... | @@ -1286,7 +1286,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 1286 | } | 1286 | } |
| 1287 | } | 1287 | } |
| 1288 | 1288 | ||
| 1289 | pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [count]Register) !void { | 1289 | pub fn spillRegisters(self: *Self, registers: []const Register) !void { |
| 1290 | for (registers) |reg| { | 1290 | for (registers) |reg| { |
| 1291 | try self.register_manager.getReg(reg, null); | 1291 | try self.register_manager.getReg(reg, null); |
| 1292 | } | 1292 | } |
| ... | @@ -1540,7 +1540,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1540,7 +1540,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1540 | break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); | 1540 | break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 1541 | } | 1541 | } |
| 1542 | 1542 | ||
| 1543 | try self.spillRegisters(2, .{ .rax, .rdx }); | 1543 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1544 | 1544 | ||
| 1545 | const lhs = try self.resolveInst(bin_op.lhs); | 1545 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1546 | const rhs = try self.resolveInst(bin_op.rhs); | 1546 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | @@ -1594,7 +1594,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1594,7 +1594,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1594 | try self.spillEflagsIfOccupied(); | 1594 | try self.spillEflagsIfOccupied(); |
| 1595 | 1595 | ||
| 1596 | if (tag == .shl_with_overflow) { | 1596 | if (tag == .shl_with_overflow) { |
| 1597 | try self.spillRegisters(1, .{.rcx}); | 1597 | try self.spillRegisters(&.{.rcx}); |
| 1598 | } | 1598 | } |
| 1599 | 1599 | ||
| 1600 | const partial: MCValue = switch (tag) { | 1600 | const partial: MCValue = switch (tag) { |
| ... | @@ -1721,7 +1721,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1721,7 +1721,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1721 | try self.spillEflagsIfOccupied(); | 1721 | try self.spillEflagsIfOccupied(); |
| 1722 | self.eflags_inst = inst; | 1722 | self.eflags_inst = inst; |
| 1723 | 1723 | ||
| 1724 | try self.spillRegisters(2, .{ .rax, .rdx }); | 1724 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1725 | 1725 | ||
| 1726 | const lhs = try self.resolveInst(bin_op.lhs); | 1726 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1727 | const rhs = try self.resolveInst(bin_op.rhs); | 1727 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | @@ -1774,7 +1774,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1774,7 +1774,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1774 | break :dst_reg dst_reg; | 1774 | break :dst_reg dst_reg; |
| 1775 | }, | 1775 | }, |
| 1776 | .unsigned => { | 1776 | .unsigned => { |
| 1777 | try self.spillRegisters(2, .{ .rax, .rdx }); | 1777 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1778 | 1778 | ||
| 1779 | const lhs = try self.resolveInst(bin_op.lhs); | 1779 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1780 | const rhs = try self.resolveInst(bin_op.rhs); | 1780 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | @@ -1888,7 +1888,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1888,7 +1888,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1888 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 1888 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1889 | } | 1889 | } |
| 1890 | 1890 | ||
| 1891 | try self.spillRegisters(1, .{.rcx}); | 1891 | try self.spillRegisters(&.{.rcx}); |
| 1892 | 1892 | ||
| 1893 | const tag = self.air.instructions.items(.tag)[inst]; | 1893 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1894 | const lhs = try self.resolveInst(bin_op.lhs); | 1894 | const lhs = try self.resolveInst(bin_op.lhs); |
| ... | @@ -2832,6 +2832,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2832,6 +2832,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2832 | .unreach => unreachable, | 2832 | .unreach => unreachable, |
| 2833 | .eflags => unreachable, | 2833 | .eflags => unreachable, |
| 2834 | .undef => { | 2834 | .undef => { |
| 2835 | if (!self.wantSafety()) return; // The already existing value will do just fine. | ||
| 2835 | switch (abi_size) { | 2836 | switch (abi_size) { |
| 2836 | 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty), | 2837 | 1 => try self.store(ptr, .{ .immediate = 0xaa }, ptr_ty, value_ty), |
| 2837 | 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty), | 2838 | 2 => try self.store(ptr, .{ .immediate = 0xaaaa }, ptr_ty, value_ty), |
| ... | @@ -4035,11 +4036,40 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4035,11 +4036,40 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4035 | defer info.deinit(self); | 4036 | defer info.deinit(self); |
| 4036 | 4037 | ||
| 4037 | try self.spillEflagsIfOccupied(); | 4038 | try self.spillEflagsIfOccupied(); |
| 4039 | try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*)); | ||
| 4038 | 4040 | ||
| 4039 | for (abi.getCallerPreservedRegs(self.target.*)) |reg| { | 4041 | // set stack arguments first because this can clobber registers |
| 4040 | try self.register_manager.getReg(reg, null); | 4042 | // also clobber spill arguments as we go |
| 4043 | if (info.return_value == .stack_offset) { | ||
| 4044 | try self.spillRegisters(&.{abi.getCAbiIntParamRegs(self.target.*)[0]}); | ||
| 4045 | } | ||
| 4046 | for (args, info.args) |arg, mc_arg| { | ||
| 4047 | const arg_ty = self.air.typeOf(arg); | ||
| 4048 | const arg_mcv = try self.resolveInst(arg); | ||
| 4049 | // Here we do not use setRegOrMem even though the logic is similar, because | ||
| 4050 | // the function call will move the stack pointer, so the offsets are different. | ||
| 4051 | switch (mc_arg) { | ||
| 4052 | .none => {}, | ||
| 4053 | .register => |reg| try self.spillRegisters(&.{reg}), | ||
| 4054 | .stack_offset => |off| { | ||
| 4055 | // TODO rewrite using `genSetStack` | ||
| 4056 | try self.genSetStackArg(arg_ty, off, arg_mcv); | ||
| 4057 | }, | ||
| 4058 | .ptr_stack_offset => { | ||
| 4059 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | ||
| 4060 | }, | ||
| 4061 | .undef => unreachable, | ||
| 4062 | .immediate => unreachable, | ||
| 4063 | .unreach => unreachable, | ||
| 4064 | .dead => unreachable, | ||
| 4065 | .memory => unreachable, | ||
| 4066 | .linker_load => unreachable, | ||
| 4067 | .eflags => unreachable, | ||
| 4068 | .register_overflow => unreachable, | ||
| 4069 | } | ||
| 4041 | } | 4070 | } |
| 4042 | 4071 | ||
| 4072 | // now we are free to set register arguments | ||
| 4043 | const ret_reg_lock: ?RegisterLock = blk: { | 4073 | const ret_reg_lock: ?RegisterLock = blk: { |
| 4044 | if (info.return_value == .stack_offset) { | 4074 | if (info.return_value == .stack_offset) { |
| 4045 | const ret_ty = fn_ty.fnReturnType(); | 4075 | const ret_ty = fn_ty.fnReturnType(); |
| ... | @@ -4049,7 +4079,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4049,7 +4079,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4049 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); | 4079 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); |
| 4050 | 4080 | ||
| 4051 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; | 4081 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; |
| 4052 | try self.register_manager.getReg(ret_reg, null); | ||
| 4053 | try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset }); | 4082 | try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset }); |
| 4054 | const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg); | 4083 | const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg); |
| 4055 | 4084 | ||
| ... | @@ -4061,25 +4090,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4061,25 +4090,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4061 | }; | 4090 | }; |
| 4062 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); | 4091 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 4063 | 4092 | ||
| 4064 | for (args, info.args) |arg, info_arg| { | 4093 | for (args, info.args) |arg, mc_arg| { |
| 4065 | const mc_arg = info_arg; | ||
| 4066 | const arg_ty = self.air.typeOf(arg); | 4094 | const arg_ty = self.air.typeOf(arg); |
| 4067 | const arg_mcv = try self.resolveInst(arg); | 4095 | const arg_mcv = try self.resolveInst(arg); |
| 4068 | // Here we do not use setRegOrMem even though the logic is similar, because | ||
| 4069 | // the function call will move the stack pointer, so the offsets are different. | ||
| 4070 | switch (mc_arg) { | 4096 | switch (mc_arg) { |
| 4071 | .none => continue, | 4097 | .none, .stack_offset, .ptr_stack_offset => {}, |
| 4072 | .register => |reg| { | 4098 | .register => |reg| try self.genSetReg(arg_ty, reg, arg_mcv), |
| 4073 | try self.register_manager.getReg(reg, null); | ||
| 4074 | try self.genSetReg(arg_ty, reg, arg_mcv); | ||
| 4075 | }, | ||
| 4076 | .stack_offset => |off| { | ||
| 4077 | // TODO rewrite using `genSetStack` | ||
| 4078 | try self.genSetStackArg(arg_ty, off, arg_mcv); | ||
| 4079 | }, | ||
| 4080 | .ptr_stack_offset => { | ||
| 4081 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | ||
| 4082 | }, | ||
| 4083 | .undef => unreachable, | 4099 | .undef => unreachable, |
| 4084 | .immediate => unreachable, | 4100 | .immediate => unreachable, |
| 4085 | .unreach => unreachable, | 4101 | .unreach => unreachable, |
| ... | @@ -5277,6 +5293,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -5277,6 +5293,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5277 | .dead => unreachable, | 5293 | .dead => unreachable, |
| 5278 | .unreach, .none => return, | 5294 | .unreach, .none => return, |
| 5279 | .undef => { | 5295 | .undef => { |
| 5296 | if (!self.wantSafety()) return; // The already existing value will do just fine. | ||
| 5280 | if (abi_size <= 8) { | 5297 | if (abi_size <= 8) { |
| 5281 | const reg = try self.copyToTmpRegister(ty, mcv); | 5298 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 5282 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); | 5299 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| ... | @@ -5384,8 +5401,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5384,8 +5401,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5384 | .dead => unreachable, | 5401 | .dead => unreachable, |
| 5385 | .unreach, .none => return, // Nothing to do. | 5402 | .unreach, .none => return, // Nothing to do. |
| 5386 | .undef => { | 5403 | .undef => { |
| 5387 | if (!self.wantSafety()) | 5404 | if (!self.wantSafety()) return; // The already existing value will do just fine. |
| 5388 | return; // The already existing value will do just fine. | ||
| 5389 | // TODO Upgrade this to a memset call when we have that available. | 5405 | // TODO Upgrade this to a memset call when we have that available. |
| 5390 | switch (abi_size) { | 5406 | switch (abi_size) { |
| 5391 | 1, 2, 4 => { | 5407 | 1, 2, 4 => { |
| ... | @@ -5607,19 +5623,14 @@ fn genInlineMemcpy( | ... | @@ -5607,19 +5623,14 @@ fn genInlineMemcpy( |
| 5607 | null; | 5623 | null; |
| 5608 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | 5624 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); |
| 5609 | 5625 | ||
| 5610 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); | 5626 | try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); |
| 5611 | const dst_addr_reg = regs[0]; | ||
| 5612 | const src_addr_reg = regs[1]; | ||
| 5613 | const index_reg = regs[2].to64(); | ||
| 5614 | const count_reg = regs[3].to64(); | ||
| 5615 | const tmp_reg = regs[4].to8(); | ||
| 5616 | 5627 | ||
| 5617 | switch (dst_ptr) { | 5628 | switch (dst_ptr) { |
| 5618 | .memory, .linker_load => { | 5629 | .memory, .linker_load => { |
| 5619 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); | 5630 | try self.loadMemPtrIntoRegister(.rdi, Type.usize, dst_ptr); |
| 5620 | }, | 5631 | }, |
| 5621 | .ptr_stack_offset, .stack_offset => |off| { | 5632 | .ptr_stack_offset, .stack_offset => |off| { |
| 5622 | try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{ | 5633 | try self.asmRegisterMemory(.lea, .rdi, Memory.sib(.qword, .{ |
| 5623 | .base = opts.dest_stack_base orelse .rbp, | 5634 | .base = opts.dest_stack_base orelse .rbp, |
| 5624 | .disp = -off, | 5635 | .disp = -off, |
| 5625 | })); | 5636 | })); |
| ... | @@ -5627,7 +5638,7 @@ fn genInlineMemcpy( | ... | @@ -5627,7 +5638,7 @@ fn genInlineMemcpy( |
| 5627 | .register => |reg| { | 5638 | .register => |reg| { |
| 5628 | try self.asmRegisterRegister( | 5639 | try self.asmRegisterRegister( |
| 5629 | .mov, | 5640 | .mov, |
| 5630 | registerAlias(dst_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), | 5641 | registerAlias(.rdi, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5631 | reg, | 5642 | reg, |
| 5632 | ); | 5643 | ); |
| 5633 | }, | 5644 | }, |
| ... | @@ -5638,10 +5649,10 @@ fn genInlineMemcpy( | ... | @@ -5638,10 +5649,10 @@ fn genInlineMemcpy( |
| 5638 | 5649 | ||
| 5639 | switch (src_ptr) { | 5650 | switch (src_ptr) { |
| 5640 | .memory, .linker_load => { | 5651 | .memory, .linker_load => { |
| 5641 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); | 5652 | try self.loadMemPtrIntoRegister(.rsi, Type.usize, src_ptr); |
| 5642 | }, | 5653 | }, |
| 5643 | .ptr_stack_offset, .stack_offset => |off| { | 5654 | .ptr_stack_offset, .stack_offset => |off| { |
| 5644 | try self.asmRegisterMemory(.lea, src_addr_reg.to64(), Memory.sib(.qword, .{ | 5655 | try self.asmRegisterMemory(.lea, .rsi, Memory.sib(.qword, .{ |
| 5645 | .base = opts.source_stack_base orelse .rbp, | 5656 | .base = opts.source_stack_base orelse .rbp, |
| 5646 | .disp = -off, | 5657 | .disp = -off, |
| 5647 | })); | 5658 | })); |
| ... | @@ -5649,7 +5660,7 @@ fn genInlineMemcpy( | ... | @@ -5649,7 +5660,7 @@ fn genInlineMemcpy( |
| 5649 | .register => |reg| { | 5660 | .register => |reg| { |
| 5650 | try self.asmRegisterRegister( | 5661 | try self.asmRegisterRegister( |
| 5651 | .mov, | 5662 | .mov, |
| 5652 | registerAlias(src_addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), | 5663 | registerAlias(.rsi, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5653 | reg, | 5664 | reg, |
| 5654 | ); | 5665 | ); |
| 5655 | }, | 5666 | }, |
| ... | @@ -5658,37 +5669,12 @@ fn genInlineMemcpy( | ... | @@ -5658,37 +5669,12 @@ fn genInlineMemcpy( |
| 5658 | }, | 5669 | }, |
| 5659 | } | 5670 | } |
| 5660 | 5671 | ||
| 5661 | try self.genSetReg(Type.usize, count_reg, len); | 5672 | try self.genSetReg(Type.usize, .rcx, len); |
| 5662 | try self.asmRegisterImmediate(.mov, index_reg, Immediate.u(0)); | 5673 | _ = try self.addInst(.{ |
| 5663 | const loop_start = try self.addInst(.{ | 5674 | .tag = .movs, |
| 5664 | .tag = .cmp, | 5675 | .ops = .string, |
| 5665 | .ops = .ri_u, | 5676 | .data = .{ .string = .{ .repeat = .rep, .width = .b } }, |
| 5666 | .data = .{ .ri = .{ | ||
| 5667 | .r1 = count_reg, | ||
| 5668 | .imm = 0, | ||
| 5669 | } }, | ||
| 5670 | }); | 5677 | }); |
| 5671 | const loop_reloc = try self.asmJccReloc(undefined, .e); | ||
| 5672 | try self.asmRegisterMemory(.mov, tmp_reg.to8(), Memory.sib(.byte, .{ | ||
| 5673 | .base = src_addr_reg, | ||
| 5674 | .scale_index = .{ | ||
| 5675 | .scale = 1, | ||
| 5676 | .index = index_reg, | ||
| 5677 | }, | ||
| 5678 | .disp = 0, | ||
| 5679 | })); | ||
| 5680 | try self.asmMemoryRegister(.mov, Memory.sib(.byte, .{ | ||
| 5681 | .base = dst_addr_reg, | ||
| 5682 | .scale_index = .{ | ||
| 5683 | .scale = 1, | ||
| 5684 | .index = index_reg, | ||
| 5685 | }, | ||
| 5686 | .disp = 0, | ||
| 5687 | }), tmp_reg.to8()); | ||
| 5688 | try self.asmRegisterImmediate(.add, index_reg, Immediate.u(1)); | ||
| 5689 | try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); | ||
| 5690 | _ = try self.asmJmpReloc(loop_start); | ||
| 5691 | try self.performReloc(loop_reloc); | ||
| 5692 | } | 5678 | } |
| 5693 | 5679 | ||
| 5694 | fn genInlineMemset( | 5680 | fn genInlineMemset( |
| ... | @@ -5698,28 +5684,20 @@ fn genInlineMemset( | ... | @@ -5698,28 +5684,20 @@ fn genInlineMemset( |
| 5698 | len: MCValue, | 5684 | len: MCValue, |
| 5699 | opts: InlineMemcpyOpts, | 5685 | opts: InlineMemcpyOpts, |
| 5700 | ) InnerError!void { | 5686 | ) InnerError!void { |
| 5701 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| | ||
| 5702 | self.register_manager.lockReg(reg) | ||
| 5703 | else | ||
| 5704 | null; | ||
| 5705 | defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg); | ||
| 5706 | |||
| 5707 | const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| | 5687 | const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| |
| 5708 | self.register_manager.lockReg(reg) | 5688 | self.register_manager.lockReg(reg) |
| 5709 | else | 5689 | else |
| 5710 | null; | 5690 | null; |
| 5711 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | 5691 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); |
| 5712 | 5692 | ||
| 5713 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | 5693 | try self.spillRegisters(&.{ .rdi, .al, .rcx }); |
| 5714 | const addr_reg = regs[0]; | ||
| 5715 | const index_reg = regs[1].to64(); | ||
| 5716 | 5694 | ||
| 5717 | switch (dst_ptr) { | 5695 | switch (dst_ptr) { |
| 5718 | .memory, .linker_load => { | 5696 | .memory, .linker_load => { |
| 5719 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); | 5697 | try self.loadMemPtrIntoRegister(.rdi, Type.usize, dst_ptr); |
| 5720 | }, | 5698 | }, |
| 5721 | .ptr_stack_offset, .stack_offset => |off| { | 5699 | .ptr_stack_offset, .stack_offset => |off| { |
| 5722 | try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ | 5700 | try self.asmRegisterMemory(.lea, .rdi, Memory.sib(.qword, .{ |
| 5723 | .base = opts.dest_stack_base orelse .rbp, | 5701 | .base = opts.dest_stack_base orelse .rbp, |
| 5724 | .disp = -off, | 5702 | .disp = -off, |
| 5725 | })); | 5703 | })); |
| ... | @@ -5727,48 +5705,22 @@ fn genInlineMemset( | ... | @@ -5727,48 +5705,22 @@ fn genInlineMemset( |
| 5727 | .register => |reg| { | 5705 | .register => |reg| { |
| 5728 | try self.asmRegisterRegister( | 5706 | try self.asmRegisterRegister( |
| 5729 | .mov, | 5707 | .mov, |
| 5730 | registerAlias(addr_reg, @intCast(u32, @divExact(reg.bitSize(), 8))), | 5708 | registerAlias(.rdi, @intCast(u32, @divExact(reg.bitSize(), 8))), |
| 5731 | reg, | 5709 | reg, |
| 5732 | ); | 5710 | ); |
| 5733 | }, | 5711 | }, |
| 5734 | else => { | 5712 | else => { |
| 5735 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); | 5713 | return self.fail("TODO implement memset for setting stack when dest is {}", .{dst_ptr}); |
| 5736 | }, | 5714 | }, |
| 5737 | } | 5715 | } |
| 5738 | 5716 | ||
| 5739 | try self.genSetReg(Type.usize, index_reg, len); | 5717 | try self.genSetReg(Type.u8, .al, value); |
| 5740 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); | 5718 | try self.genSetReg(Type.usize, .rcx, len); |
| 5741 | 5719 | _ = try self.addInst(.{ | |
| 5742 | const loop_start = try self.addInst(.{ | 5720 | .tag = .stos, |
| 5743 | .tag = .cmp, | 5721 | .ops = .string, |
| 5744 | .ops = .ri_s, | 5722 | .data = .{ .string = .{ .repeat = .rep, .width = .b } }, |
| 5745 | .data = .{ .ri = .{ | ||
| 5746 | .r1 = index_reg, | ||
| 5747 | .imm = @bitCast(u32, @as(i32, -1)), | ||
| 5748 | } }, | ||
| 5749 | }); | 5723 | }); |
| 5750 | const loop_reloc = try self.asmJccReloc(undefined, .e); | ||
| 5751 | |||
| 5752 | switch (value) { | ||
| 5753 | .immediate => |x| { | ||
| 5754 | if (x > math.maxInt(i32)) { | ||
| 5755 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); | ||
| 5756 | } | ||
| 5757 | try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ | ||
| 5758 | .base = addr_reg, | ||
| 5759 | .scale_index = .{ | ||
| 5760 | .scale = 1, | ||
| 5761 | .index = index_reg, | ||
| 5762 | }, | ||
| 5763 | .disp = 0, | ||
| 5764 | }), Immediate.u(@intCast(u8, x))); | ||
| 5765 | }, | ||
| 5766 | else => return self.fail("TODO inline memset for value of type {}", .{value}), | ||
| 5767 | } | ||
| 5768 | |||
| 5769 | try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); | ||
| 5770 | _ = try self.asmJmpReloc(loop_start); | ||
| 5771 | try self.performReloc(loop_reloc); | ||
| 5772 | } | 5724 | } |
| 5773 | 5725 | ||
| 5774 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { | 5726 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| ... | @@ -5788,8 +5740,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5788,8 +5740,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5788 | }, | 5740 | }, |
| 5789 | .unreach, .none => return, // Nothing to do. | 5741 | .unreach, .none => return, // Nothing to do. |
| 5790 | .undef => { | 5742 | .undef => { |
| 5791 | if (!self.wantSafety()) | 5743 | if (!self.wantSafety()) return; // The already existing value will do just fine. |
| 5792 | return; // The already existing value will do just fine. | ||
| 5793 | // Write the debug undefined value. | 5744 | // Write the debug undefined value. |
| 5794 | switch (registerAlias(reg, abi_size).bitSize()) { | 5745 | switch (registerAlias(reg, abi_size).bitSize()) { |
| 5795 | 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), | 5746 | 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), |
| ... | @@ -5802,27 +5753,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5802,27 +5753,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5802 | .eflags => |cc| { | 5753 | .eflags => |cc| { |
| 5803 | return self.asmSetccRegister(reg.to8(), cc); | 5754 | return self.asmSetccRegister(reg.to8(), cc); |
| 5804 | }, | 5755 | }, |
| 5805 | .immediate => |x| { | 5756 | .immediate => |imm| { |
| 5806 | if (x == 0) { | 5757 | if (imm == 0) { |
| 5807 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit | 5758 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit |
| 5808 | // register is the fastest way to zero a register. | 5759 | // register is the fastest way to zero a register. |
| 5809 | return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); | 5760 | try self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); |
| 5810 | } | 5761 | } else if (abi_size > 4 and math.cast(u32, imm) != null) { |
| 5811 | if (ty.isSignedInt()) { | 5762 | // 32-bit moves zero-extend to 64-bit. |
| 5812 | const signed_x = @bitCast(i64, x); | 5763 | try self.asmRegisterImmediate(.mov, reg.to32(), Immediate.u(imm)); |
| 5813 | if (math.minInt(i32) <= signed_x and signed_x <= math.maxInt(i32)) { | 5764 | } else if (abi_size <= 4 and @bitCast(i64, imm) < 0) { |
| 5814 | return self.asmRegisterImmediate( | 5765 | try self.asmRegisterImmediate( |
| 5815 | .mov, | 5766 | .mov, |
| 5816 | registerAlias(reg, abi_size), | 5767 | registerAlias(reg, abi_size), |
| 5817 | Immediate.s(@intCast(i32, signed_x)), | 5768 | Immediate.s(@intCast(i32, @bitCast(i64, imm))), |
| 5818 | ); | 5769 | ); |
| 5819 | } | 5770 | } else { |
| 5771 | try self.asmRegisterImmediate( | ||
| 5772 | .mov, | ||
| 5773 | registerAlias(reg, abi_size), | ||
| 5774 | Immediate.u(imm), | ||
| 5775 | ); | ||
| 5820 | } | 5776 | } |
| 5821 | return self.asmRegisterImmediate( | ||
| 5822 | .mov, | ||
| 5823 | registerAlias(reg, abi_size), | ||
| 5824 | Immediate.u(x), | ||
| 5825 | ); | ||
| 5826 | }, | 5777 | }, |
| 5827 | .register => |src_reg| { | 5778 | .register => |src_reg| { |
| 5828 | // If the registers are the same, nothing to do. | 5779 | // If the registers are the same, nothing to do. |
| ... | @@ -6136,7 +6087,7 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6136,7 +6087,7 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 6136 | 6087 | ||
| 6137 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { | 6088 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { |
| 6138 | _ = inst; | 6089 | _ = inst; |
| 6139 | return self.fail("TODO implement x86 airAtomicRaw", .{}); | 6090 | return self.fail("TODO implement x86 airAtomicRmw", .{}); |
| 6140 | } | 6091 | } |
| 6141 | 6092 | ||
| 6142 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { | 6093 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -6177,7 +6128,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6177,7 +6128,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { |
| 6177 | 6128 | ||
| 6178 | try self.genInlineMemset(dst_ptr, src_val, len, .{}); | 6129 | try self.genInlineMemset(dst_ptr, src_val, len, .{}); |
| 6179 | 6130 | ||
| 6180 | return self.finishAir(inst, .none, .{ pl_op.operand, .none, .none }); | 6131 | return self.finishAir(inst, .none, .{ pl_op.operand, extra.lhs, extra.rhs }); |
| 6181 | } | 6132 | } |
| 6182 | 6133 | ||
| 6183 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | 6134 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -6229,7 +6180,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6229,7 +6180,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6229 | 6180 | ||
| 6230 | try self.genInlineMemcpy(dst_ptr, src, len, .{}); | 6181 | try self.genInlineMemcpy(dst_ptr, src, len, .{}); |
| 6231 | 6182 | ||
| 6232 | return self.finishAir(inst, .none, .{ pl_op.operand, .none, .none }); | 6183 | return self.finishAir(inst, .none, .{ pl_op.operand, extra.lhs, extra.rhs }); |
| 6233 | } | 6184 | } |
| 6234 | 6185 | ||
| 6235 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { | 6186 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
src/arch/x86_64/Emit.zig+34-16| ... | @@ -130,6 +130,13 @@ pub fn lowerMir(emit: *Emit) InnerError!void { | ... | @@ -130,6 +130,13 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 130 | .ucomisd, | 130 | .ucomisd, |
| 131 | => try emit.mirEncodeGeneric(tag, inst), | 131 | => try emit.mirEncodeGeneric(tag, inst), |
| 132 | 132 | ||
| 133 | .cmps, | ||
| 134 | .lods, | ||
| 135 | .movs, | ||
| 136 | .scas, | ||
| 137 | .stos, | ||
| 138 | => try emit.mirString(tag, inst), | ||
| 139 | |||
| 133 | .jmp_reloc => try emit.mirJmpReloc(inst), | 140 | .jmp_reloc => try emit.mirJmpReloc(inst), |
| 134 | 141 | ||
| 135 | .call_extern => try emit.mirCallExtern(inst), | 142 | .call_extern => try emit.mirCallExtern(inst), |
| ... | @@ -183,18 +190,8 @@ fn fixupRelocs(emit: *Emit) InnerError!void { | ... | @@ -183,18 +190,8 @@ fn fixupRelocs(emit: *Emit) InnerError!void { |
| 183 | } | 190 | } |
| 184 | } | 191 | } |
| 185 | 192 | ||
| 186 | fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct { | 193 | fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: Instruction.Init) InnerError!void { |
| 187 | op1: Instruction.Operand = .none, | 194 | const inst = try Instruction.new(mnemonic, ops); |
| 188 | op2: Instruction.Operand = .none, | ||
| 189 | op3: Instruction.Operand = .none, | ||
| 190 | op4: Instruction.Operand = .none, | ||
| 191 | }) InnerError!void { | ||
| 192 | const inst = try Instruction.new(mnemonic, .{ | ||
| 193 | .op1 = ops.op1, | ||
| 194 | .op2 = ops.op2, | ||
| 195 | .op3 = ops.op3, | ||
| 196 | .op4 = ops.op4, | ||
| 197 | }); | ||
| 198 | return inst.encode(emit.code.writer()); | 195 | return inst.encode(emit.code.writer()); |
| 199 | } | 196 | } |
| 200 | 197 | ||
| ... | @@ -318,6 +315,28 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE | ... | @@ -318,6 +315,28 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE |
| 318 | }); | 315 | }); |
| 319 | } | 316 | } |
| 320 | 317 | ||
| 318 | fn mirString(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { | ||
| 319 | const ops = emit.mir.instructions.items(.ops)[inst]; | ||
| 320 | switch (ops) { | ||
| 321 | .string => { | ||
| 322 | const data = emit.mir.instructions.items(.data)[inst].string; | ||
| 323 | const mnemonic = switch (tag) { | ||
| 324 | inline .cmps, .lods, .movs, .scas, .stos => |comptime_tag| switch (data.width) { | ||
| 325 | inline else => |comptime_width| @field( | ||
| 326 | Instruction.Mnemonic, | ||
| 327 | @tagName(comptime_tag) ++ @tagName(comptime_width), | ||
| 328 | ), | ||
| 329 | }, | ||
| 330 | else => unreachable, | ||
| 331 | }; | ||
| 332 | return emit.encode(mnemonic, .{ .prefix = switch (data.repeat) { | ||
| 333 | inline else => |comptime_repeat| @field(Instruction.Prefix, @tagName(comptime_repeat)), | ||
| 334 | } }); | ||
| 335 | }, | ||
| 336 | else => unreachable, | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 321 | fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 340 | fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 322 | const ops = emit.mir.instructions.items(.ops)[inst]; | 341 | const ops = emit.mir.instructions.items(.ops)[inst]; |
| 323 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 342 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| ... | @@ -377,10 +396,9 @@ fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -377,10 +396,9 @@ fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 377 | } | 396 | } |
| 378 | 397 | ||
| 379 | fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { | 398 | fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { |
| 380 | inline for (@typeInfo(bits.Condition).Enum.fields) |field| { | 399 | return switch (cc) { |
| 381 | if (mem.eql(u8, field.name, @tagName(cc))) | 400 | inline else => |comptime_cc| @field(Instruction.Mnemonic, basename ++ @tagName(comptime_cc)), |
| 382 | return @field(Instruction.Mnemonic, basename ++ field.name); | 401 | }; |
| 383 | } else unreachable; | ||
| 384 | } | 402 | } |
| 385 | 403 | ||
| 386 | fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 404 | fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
src/arch/x86_64/Encoding.zig+24-24| ... | @@ -24,12 +24,7 @@ opc: [7]u8, | ... | @@ -24,12 +24,7 @@ opc: [7]u8, |
| 24 | modrm_ext: u3, | 24 | modrm_ext: u3, |
| 25 | mode: Mode, | 25 | mode: Mode, |
| 26 | 26 | ||
| 27 | pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { | 27 | pub fn findByMnemonic(mnemonic: Mnemonic, args: Instruction.Init) !?Encoding { |
| 28 | op1: Instruction.Operand, | ||
| 29 | op2: Instruction.Operand, | ||
| 30 | op3: Instruction.Operand, | ||
| 31 | op4: Instruction.Operand, | ||
| 32 | }) !?Encoding { | ||
| 33 | const input_op1 = Op.fromOperand(args.op1); | 28 | const input_op1 = Op.fromOperand(args.op1); |
| 34 | const input_op2 = Op.fromOperand(args.op2); | 29 | const input_op2 = Op.fromOperand(args.op2); |
| 35 | const input_op3 = Op.fromOperand(args.op3); | 30 | const input_op3 = Op.fromOperand(args.op3); |
| ... | @@ -109,17 +104,13 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { | ... | @@ -109,17 +104,13 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { |
| 109 | if (count == 1) return candidates[0]; | 104 | if (count == 1) return candidates[0]; |
| 110 | 105 | ||
| 111 | const EncodingLength = struct { | 106 | const EncodingLength = struct { |
| 112 | fn estimate(encoding: Encoding, params: struct { | 107 | fn estimate(encoding: Encoding, params: Instruction.Init) usize { |
| 113 | op1: Instruction.Operand, | ||
| 114 | op2: Instruction.Operand, | ||
| 115 | op3: Instruction.Operand, | ||
| 116 | op4: Instruction.Operand, | ||
| 117 | }) usize { | ||
| 118 | var inst = Instruction{ | 108 | var inst = Instruction{ |
| 119 | .op1 = params.op1, | 109 | .op1 = params.op1, |
| 120 | .op2 = params.op2, | 110 | .op2 = params.op2, |
| 121 | .op3 = params.op3, | 111 | .op3 = params.op3, |
| 122 | .op4 = params.op4, | 112 | .op4 = params.op4, |
| 113 | .prefix = params.prefix, | ||
| 123 | .encoding = encoding, | 114 | .encoding = encoding, |
| 124 | }; | 115 | }; |
| 125 | var cwriter = std.io.countingWriter(std.io.null_writer); | 116 | var cwriter = std.io.countingWriter(std.io.null_writer); |
| ... | @@ -140,12 +131,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { | ... | @@ -140,12 +131,7 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: struct { |
| 140 | else => {}, | 131 | else => {}, |
| 141 | } | 132 | } |
| 142 | 133 | ||
| 143 | const len = EncodingLength.estimate(candidate, .{ | 134 | const len = EncodingLength.estimate(candidate, args); |
| 144 | .op1 = args.op1, | ||
| 145 | .op2 = args.op2, | ||
| 146 | .op3 = args.op3, | ||
| 147 | .op4 = args.op4, | ||
| 148 | }); | ||
| 149 | const current = shortest_encoding orelse { | 135 | const current = shortest_encoding orelse { |
| 150 | shortest_encoding = .{ .index = i, .len = len }; | 136 | shortest_encoding = .{ .index = i, .len = len }; |
| 151 | continue; | 137 | continue; |
| ... | @@ -228,7 +214,11 @@ pub fn modRmExt(encoding: Encoding) u3 { | ... | @@ -228,7 +214,11 @@ pub fn modRmExt(encoding: Encoding) u3 { |
| 228 | } | 214 | } |
| 229 | 215 | ||
| 230 | pub fn operandBitSize(encoding: Encoding) u64 { | 216 | pub fn operandBitSize(encoding: Encoding) u64 { |
| 231 | if (encoding.mode == .long) return 64; | 217 | switch (encoding.mode) { |
| 218 | .short => return 16, | ||
| 219 | .long => return 64, | ||
| 220 | else => {}, | ||
| 221 | } | ||
| 232 | const bit_size: u64 = switch (encoding.op_en) { | 222 | const bit_size: u64 = switch (encoding.op_en) { |
| 233 | .np => switch (encoding.op1) { | 223 | .np => switch (encoding.op1) { |
| 234 | .o16 => 16, | 224 | .o16 => 16, |
| ... | @@ -317,10 +307,13 @@ pub const Mnemonic = enum { | ... | @@ -317,10 +307,13 @@ pub const Mnemonic = enum { |
| 317 | // zig fmt: off | 307 | // zig fmt: off |
| 318 | // General-purpose | 308 | // General-purpose |
| 319 | adc, add, @"and", | 309 | adc, add, @"and", |
| 320 | call, cbw, cwde, cdqe, cwd, cdq, cqo, cmp, | 310 | call, cbw, cdq, cdqe, |
| 321 | cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna, | 311 | cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna, |
| 322 | cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno, | 312 | cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno, |
| 323 | cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz, | 313 | cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz, |
| 314 | cmp, | ||
| 315 | cmps, cmpsb, cmpsd, cmpsq, cmpsw, | ||
| 316 | cqo, cwd, cwde, | ||
| 324 | div, | 317 | div, |
| 325 | fisttp, fld, | 318 | fisttp, fld, |
| 326 | idiv, imul, int3, | 319 | idiv, imul, int3, |
| ... | @@ -328,15 +321,21 @@ pub const Mnemonic = enum { | ... | @@ -328,15 +321,21 @@ pub const Mnemonic = enum { |
| 328 | jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz, | 321 | jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz, |
| 329 | jmp, | 322 | jmp, |
| 330 | lea, | 323 | lea, |
| 331 | mov, movsx, movsxd, movzx, mul, | 324 | lods, lodsb, lodsd, lodsq, lodsw, |
| 325 | mov, | ||
| 326 | movs, movsb, movsd, movsq, movsw, | ||
| 327 | movsx, movsxd, movzx, mul, | ||
| 332 | nop, | 328 | nop, |
| 333 | @"or", | 329 | @"or", |
| 334 | pop, push, | 330 | pop, push, |
| 335 | ret, | 331 | ret, |
| 336 | sal, sar, sbb, shl, shr, sub, syscall, | 332 | sal, sar, sbb, |
| 333 | scas, scasb, scasd, scasq, scasw, | ||
| 334 | shl, shr, sub, syscall, | ||
| 337 | seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae, | 335 | seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae, |
| 338 | setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns, | 336 | setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns, |
| 339 | setnz, seto, setp, setpe, setpo, sets, setz, | 337 | setnz, seto, setp, setpe, setpo, sets, setz, |
| 338 | stos, stosb, stosd, stosq, stosw, | ||
| 340 | @"test", | 339 | @"test", |
| 341 | ud2, | 340 | ud2, |
| 342 | xor, | 341 | xor, |
| ... | @@ -351,10 +350,10 @@ pub const Mnemonic = enum { | ... | @@ -351,10 +350,10 @@ pub const Mnemonic = enum { |
| 351 | ucomiss, | 350 | ucomiss, |
| 352 | // SSE2 | 351 | // SSE2 |
| 353 | addsd, | 352 | addsd, |
| 354 | cmpsd, | 353 | //cmpsd, |
| 355 | divsd, | 354 | divsd, |
| 356 | maxsd, minsd, | 355 | maxsd, minsd, |
| 357 | movq, movsd, | 356 | movq, //movsd, |
| 358 | mulsd, | 357 | mulsd, |
| 359 | subsd, | 358 | subsd, |
| 360 | ucomisd, | 359 | ucomisd, |
| ... | @@ -591,6 +590,7 @@ pub const Op = enum { | ... | @@ -591,6 +590,7 @@ pub const Op = enum { |
| 591 | 590 | ||
| 592 | pub const Mode = enum { | 591 | pub const Mode = enum { |
| 593 | none, | 592 | none, |
| 593 | short, | ||
| 594 | fpu, | 594 | fpu, |
| 595 | rex, | 595 | rex, |
| 596 | long, | 596 | long, |
src/arch/x86_64/Mir.zig+43| ... | @@ -150,6 +150,17 @@ pub const Inst = struct { | ... | @@ -150,6 +150,17 @@ pub const Inst = struct { |
| 150 | /// Unordered compare scalar double-precision floating-point values | 150 | /// Unordered compare scalar double-precision floating-point values |
| 151 | ucomisd, | 151 | ucomisd, |
| 152 | 152 | ||
| 153 | /// Compare string operands | ||
| 154 | cmps, | ||
| 155 | /// Load string | ||
| 156 | lods, | ||
| 157 | /// Move data from string to string | ||
| 158 | movs, | ||
| 159 | /// Scan string | ||
| 160 | scas, | ||
| 161 | /// Store string | ||
| 162 | stos, | ||
| 163 | |||
| 153 | /// Conditional move | 164 | /// Conditional move |
| 154 | cmovcc, | 165 | cmovcc, |
| 155 | /// Conditional jump | 166 | /// Conditional jump |
| ... | @@ -268,6 +279,30 @@ pub const Inst = struct { | ... | @@ -268,6 +279,30 @@ pub const Inst = struct { |
| 268 | /// Memory (RIP), register operands. | 279 | /// Memory (RIP), register operands. |
| 269 | /// Uses `rx` payload with extra data of type `MemoryRip`. | 280 | /// Uses `rx` payload with extra data of type `MemoryRip`. |
| 270 | mr_rip, | 281 | mr_rip, |
| 282 | /// Single memory (SIB) operand with lock prefix. | ||
| 283 | /// Uses `payload` with extra data of type `MemorySib`. | ||
| 284 | lock_m_sib, | ||
| 285 | /// Single memory (RIP) operand with lock prefix. | ||
| 286 | /// Uses `payload` with extra data of type `MemoryRip`. | ||
| 287 | lock_m_rip, | ||
| 288 | /// Memory (SIB), immediate (unsigned) operands with lock prefix. | ||
| 289 | /// Uses `xi` payload with extra data of type `MemorySib`. | ||
| 290 | lock_mi_u_sib, | ||
| 291 | /// Memory (RIP), immediate (unsigned) operands with lock prefix. | ||
| 292 | /// Uses `xi` payload with extra data of type `MemoryRip`. | ||
| 293 | lock_mi_u_rip, | ||
| 294 | /// Memory (SIB), immediate (sign-extend) operands with lock prefix. | ||
| 295 | /// Uses `xi` payload with extra data of type `MemorySib`. | ||
| 296 | lock_mi_s_sib, | ||
| 297 | /// Memory (RIP), immediate (sign-extend) operands with lock prefix. | ||
| 298 | /// Uses `xi` payload with extra data of type `MemoryRip`. | ||
| 299 | lock_mi_s_rip, | ||
| 300 | /// Memory (SIB), register operands with lock prefix. | ||
| 301 | /// Uses `rx` payload with extra data of type `MemorySib`. | ||
| 302 | lock_mr_sib, | ||
| 303 | /// Memory (RIP), register operands with lock prefix. | ||
| 304 | /// Uses `rx` payload with extra data of type `MemoryRip`. | ||
| 305 | lock_mr_rip, | ||
| 271 | /// Rax, Memory moffs. | 306 | /// Rax, Memory moffs. |
| 272 | /// Uses `payload` with extra data of type `MemoryMoffs`. | 307 | /// Uses `payload` with extra data of type `MemoryMoffs`. |
| 273 | rax_moffs, | 308 | rax_moffs, |
| ... | @@ -280,6 +315,9 @@ pub const Inst = struct { | ... | @@ -280,6 +315,9 @@ pub const Inst = struct { |
| 280 | /// References another Mir instruction directly with condition code (CC). | 315 | /// References another Mir instruction directly with condition code (CC). |
| 281 | /// Uses `inst_cc` payload. | 316 | /// Uses `inst_cc` payload. |
| 282 | inst_cc, | 317 | inst_cc, |
| 318 | /// String repeat and width | ||
| 319 | /// Uses `string` payload. | ||
| 320 | string, | ||
| 283 | /// Uses `reloc` payload. | 321 | /// Uses `reloc` payload. |
| 284 | reloc, | 322 | reloc, |
| 285 | /// Linker relocation - GOT indirection. | 323 | /// Linker relocation - GOT indirection. |
| ... | @@ -353,6 +391,11 @@ pub const Inst = struct { | ... | @@ -353,6 +391,11 @@ pub const Inst = struct { |
| 353 | payload: u32, | 391 | payload: u32, |
| 354 | imm: u32, | 392 | imm: u32, |
| 355 | }, | 393 | }, |
| 394 | /// String instruction prefix and width. | ||
| 395 | string: struct { | ||
| 396 | repeat: bits.StringRepeat, | ||
| 397 | width: bits.StringWidth, | ||
| 398 | }, | ||
| 356 | /// Relocation for the linker where: | 399 | /// Relocation for the linker where: |
| 357 | /// * `atom_index` is the index of the source | 400 | /// * `atom_index` is the index of the source |
| 358 | /// * `sym_index` is the index of the target | 401 | /// * `sym_index` is the index of the target |
src/arch/x86_64/bits.zig+3| ... | @@ -6,6 +6,9 @@ const Allocator = std.mem.Allocator; | ... | @@ -6,6 +6,9 @@ const Allocator = std.mem.Allocator; |
| 6 | const ArrayList = std.ArrayList; | 6 | const ArrayList = std.ArrayList; |
| 7 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| 8 | 8 | ||
| 9 | pub const StringRepeat = enum(u3) { none, rep, repe, repz, repne, repnz }; | ||
| 10 | pub const StringWidth = enum(u2) { b, w, d, q }; | ||
| 11 | |||
| 9 | /// EFLAGS condition codes | 12 | /// EFLAGS condition codes |
| 10 | pub const Condition = enum(u5) { | 13 | pub const Condition = enum(u5) { |
| 11 | /// above | 14 | /// above |
src/arch/x86_64/encoder.zig+33-38| ... | @@ -15,10 +15,21 @@ pub const Instruction = struct { | ... | @@ -15,10 +15,21 @@ pub const Instruction = struct { |
| 15 | op2: Operand = .none, | 15 | op2: Operand = .none, |
| 16 | op3: Operand = .none, | 16 | op3: Operand = .none, |
| 17 | op4: Operand = .none, | 17 | op4: Operand = .none, |
| 18 | prefix: Prefix = .none, | ||
| 18 | encoding: Encoding, | 19 | encoding: Encoding, |
| 19 | 20 | ||
| 20 | pub const Mnemonic = Encoding.Mnemonic; | 21 | pub const Mnemonic = Encoding.Mnemonic; |
| 21 | 22 | ||
| 23 | pub const Prefix = enum(u3) { | ||
| 24 | none, | ||
| 25 | lock, | ||
| 26 | rep, | ||
| 27 | repe, | ||
| 28 | repz, | ||
| 29 | repne, | ||
| 30 | repnz, | ||
| 31 | }; | ||
| 32 | |||
| 22 | pub const Operand = union(enum) { | 33 | pub const Operand = union(enum) { |
| 23 | none, | 34 | none, |
| 24 | reg: Register, | 35 | reg: Register, |
| ... | @@ -96,18 +107,16 @@ pub const Instruction = struct { | ... | @@ -96,18 +107,16 @@ pub const Instruction = struct { |
| 96 | } | 107 | } |
| 97 | }; | 108 | }; |
| 98 | 109 | ||
| 99 | pub fn new(mnemonic: Mnemonic, args: struct { | 110 | pub const Init = struct { |
| 111 | prefix: Prefix = .none, | ||
| 100 | op1: Operand = .none, | 112 | op1: Operand = .none, |
| 101 | op2: Operand = .none, | 113 | op2: Operand = .none, |
| 102 | op3: Operand = .none, | 114 | op3: Operand = .none, |
| 103 | op4: Operand = .none, | 115 | op4: Operand = .none, |
| 104 | }) !Instruction { | 116 | }; |
| 105 | const encoding = (try Encoding.findByMnemonic(mnemonic, .{ | 117 | |
| 106 | .op1 = args.op1, | 118 | pub fn new(mnemonic: Mnemonic, args: Init) !Instruction { |
| 107 | .op2 = args.op2, | 119 | const encoding = (try Encoding.findByMnemonic(mnemonic, args)) orelse { |
| 108 | .op3 = args.op3, | ||
| 109 | .op4 = args.op4, | ||
| 110 | })) orelse { | ||
| 111 | log.debug("no encoding found for: {s} {s} {s} {s} {s}", .{ | 120 | log.debug("no encoding found for: {s} {s} {s} {s} {s}", .{ |
| 112 | @tagName(mnemonic), | 121 | @tagName(mnemonic), |
| 113 | @tagName(Encoding.Op.fromOperand(args.op1)), | 122 | @tagName(Encoding.Op.fromOperand(args.op1)), |
| ... | @@ -119,6 +128,7 @@ pub const Instruction = struct { | ... | @@ -119,6 +128,7 @@ pub const Instruction = struct { |
| 119 | }; | 128 | }; |
| 120 | log.debug("selected encoding: {}", .{encoding}); | 129 | log.debug("selected encoding: {}", .{encoding}); |
| 121 | return .{ | 130 | return .{ |
| 131 | .prefix = args.prefix, | ||
| 122 | .op1 = args.op1, | 132 | .op1 = args.op1, |
| 123 | .op2 = args.op2, | 133 | .op2 = args.op2, |
| 124 | .op3 = args.op3, | 134 | .op3 = args.op3, |
| ... | @@ -128,6 +138,7 @@ pub const Instruction = struct { | ... | @@ -128,6 +138,7 @@ pub const Instruction = struct { |
| 128 | } | 138 | } |
| 129 | 139 | ||
| 130 | pub fn fmtPrint(inst: Instruction, writer: anytype) !void { | 140 | pub fn fmtPrint(inst: Instruction, writer: anytype) !void { |
| 141 | if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)}); | ||
| 131 | try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); | 142 | try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); |
| 132 | const ops = [_]struct { Operand, Encoding.Op }{ | 143 | const ops = [_]struct { Operand, Encoding.Op }{ |
| 133 | .{ inst.op1, inst.encoding.op1 }, | 144 | .{ inst.op1, inst.encoding.op1 }, |
| ... | @@ -215,6 +226,14 @@ pub const Instruction = struct { | ... | @@ -215,6 +226,14 @@ pub const Instruction = struct { |
| 215 | const op_en = enc.op_en; | 226 | const op_en = enc.op_en; |
| 216 | 227 | ||
| 217 | var legacy = LegacyPrefixes{}; | 228 | var legacy = LegacyPrefixes{}; |
| 229 | |||
| 230 | switch (inst.prefix) { | ||
| 231 | .none => {}, | ||
| 232 | .lock => legacy.prefix_f0 = true, | ||
| 233 | .repne, .repnz => legacy.prefix_f2 = true, | ||
| 234 | .rep, .repe, .repz => legacy.prefix_f3 = true, | ||
| 235 | } | ||
| 236 | |||
| 218 | if (enc.mode == .none) { | 237 | if (enc.mode == .none) { |
| 219 | const bit_size = enc.operandBitSize(); | 238 | const bit_size = enc.operandBitSize(); |
| 220 | if (bit_size == 16) { | 239 | if (bit_size == 16) { |
| ... | @@ -811,15 +830,11 @@ const TestEncode = struct { | ... | @@ -811,15 +830,11 @@ const TestEncode = struct { |
| 811 | buffer: [32]u8 = undefined, | 830 | buffer: [32]u8 = undefined, |
| 812 | index: usize = 0, | 831 | index: usize = 0, |
| 813 | 832 | ||
| 814 | fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: struct { | 833 | fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { |
| 815 | op1: Instruction.Operand = .none, | ||
| 816 | op2: Instruction.Operand = .none, | ||
| 817 | op3: Instruction.Operand = .none, | ||
| 818 | op4: Instruction.Operand = .none, | ||
| 819 | }) !void { | ||
| 820 | var stream = std.io.fixedBufferStream(&enc.buffer); | 834 | var stream = std.io.fixedBufferStream(&enc.buffer); |
| 821 | var count_writer = std.io.countingWriter(stream.writer()); | 835 | var count_writer = std.io.countingWriter(stream.writer()); |
| 822 | const inst = try Instruction.new(mnemonic, .{ | 836 | const inst = try Instruction.new(mnemonic, .{ |
| 837 | .prefix = args.prefix, | ||
| 823 | .op1 = args.op1, | 838 | .op1 = args.op1, |
| 824 | .op2 = args.op2, | 839 | .op2 = args.op2, |
| 825 | .op3 = args.op3, | 840 | .op3 = args.op3, |
| ... | @@ -1447,18 +1462,8 @@ test "lower NP encoding" { | ... | @@ -1447,18 +1462,8 @@ test "lower NP encoding" { |
| 1447 | try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall"); | 1462 | try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall"); |
| 1448 | } | 1463 | } |
| 1449 | 1464 | ||
| 1450 | fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: struct { | 1465 | fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { |
| 1451 | op1: Instruction.Operand = .none, | 1466 | const err = Instruction.new(mnemonic, args); |
| 1452 | op2: Instruction.Operand = .none, | ||
| 1453 | op3: Instruction.Operand = .none, | ||
| 1454 | op4: Instruction.Operand = .none, | ||
| 1455 | }) !void { | ||
| 1456 | const err = Instruction.new(mnemonic, .{ | ||
| 1457 | .op1 = args.op1, | ||
| 1458 | .op2 = args.op2, | ||
| 1459 | .op3 = args.op3, | ||
| 1460 | .op4 = args.op4, | ||
| 1461 | }); | ||
| 1462 | try testing.expectError(error.InvalidInstruction, err); | 1467 | try testing.expectError(error.InvalidInstruction, err); |
| 1463 | } | 1468 | } |
| 1464 | 1469 | ||
| ... | @@ -1479,18 +1484,8 @@ test "invalid instruction" { | ... | @@ -1479,18 +1484,8 @@ test "invalid instruction" { |
| 1479 | try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } }); | 1484 | try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } }); |
| 1480 | } | 1485 | } |
| 1481 | 1486 | ||
| 1482 | fn cannotEncode(mnemonic: Instruction.Mnemonic, args: struct { | 1487 | fn cannotEncode(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { |
| 1483 | op1: Instruction.Operand = .none, | 1488 | try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, args)); |
| 1484 | op2: Instruction.Operand = .none, | ||
| 1485 | op3: Instruction.Operand = .none, | ||
| 1486 | op4: Instruction.Operand = .none, | ||
| 1487 | }) !void { | ||
| 1488 | try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, .{ | ||
| 1489 | .op1 = args.op1, | ||
| 1490 | .op2 = args.op2, | ||
| 1491 | .op3 = args.op3, | ||
| 1492 | .op4 = args.op4, | ||
| 1493 | })); | ||
| 1494 | } | 1489 | } |
| 1495 | 1490 | ||
| 1496 | test "cannot encode" { | 1491 | test "cannot encode" { |
src/arch/x86_64/encodings.zig+45| ... | @@ -207,6 +207,15 @@ pub const table = &[_]Entry{ | ... | @@ -207,6 +207,15 @@ pub const table = &[_]Entry{ |
| 207 | .{ .cmp, .rm, .r32, .rm32, .none, .none, &.{ 0x3b }, 0, .none }, | 207 | .{ .cmp, .rm, .r32, .rm32, .none, .none, &.{ 0x3b }, 0, .none }, |
| 208 | .{ .cmp, .rm, .r64, .rm64, .none, .none, &.{ 0x3b }, 0, .long }, | 208 | .{ .cmp, .rm, .r64, .rm64, .none, .none, &.{ 0x3b }, 0, .long }, |
| 209 | 209 | ||
| 210 | .{ .cmps, .np, .m8, .m8, .none, .none, &.{ 0xa6 }, 0, .none }, | ||
| 211 | .{ .cmps, .np, .m16, .m16, .none, .none, &.{ 0xa7 }, 0, .none }, | ||
| 212 | .{ .cmps, .np, .m32, .m32, .none, .none, &.{ 0xa7 }, 0, .none }, | ||
| 213 | .{ .cmps, .np, .m64, .m64, .none, .none, &.{ 0xa7 }, 0, .long }, | ||
| 214 | .{ .cmpsb, .np, .none, .none, .none, .none, &.{ 0xa6 }, 0, .none }, | ||
| 215 | .{ .cmpsw, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .short }, | ||
| 216 | .{ .cmpsd, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .none }, | ||
| 217 | .{ .cmpsq, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .long }, | ||
| 218 | |||
| 210 | .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .none }, | 219 | .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .none }, |
| 211 | .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .rex }, | 220 | .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .rex }, |
| 212 | .{ .div, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 6, .none }, | 221 | .{ .div, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 6, .none }, |
| ... | @@ -283,6 +292,15 @@ pub const table = &[_]Entry{ | ... | @@ -283,6 +292,15 @@ pub const table = &[_]Entry{ |
| 283 | .{ .lea, .rm, .r32, .m, .none, .none, &.{ 0x8d }, 0, .none }, | 292 | .{ .lea, .rm, .r32, .m, .none, .none, &.{ 0x8d }, 0, .none }, |
| 284 | .{ .lea, .rm, .r64, .m, .none, .none, &.{ 0x8d }, 0, .long }, | 293 | .{ .lea, .rm, .r64, .m, .none, .none, &.{ 0x8d }, 0, .long }, |
| 285 | 294 | ||
| 295 | .{ .lods, .np, .m8, .none, .none, .none, &.{ 0xac }, 0, .none }, | ||
| 296 | .{ .lods, .np, .m16, .none, .none, .none, &.{ 0xad }, 0, .none }, | ||
| 297 | .{ .lods, .np, .m32, .none, .none, .none, &.{ 0xad }, 0, .none }, | ||
| 298 | .{ .lods, .np, .m64, .none, .none, .none, &.{ 0xad }, 0, .long }, | ||
| 299 | .{ .lodsb, .np, .none, .none, .none, .none, &.{ 0xac }, 0, .none }, | ||
| 300 | .{ .lodsw, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .short }, | ||
| 301 | .{ .lodsd, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .none }, | ||
| 302 | .{ .lodsq, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .long }, | ||
| 303 | |||
| 286 | .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .none }, | 304 | .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .none }, |
| 287 | .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .rex }, | 305 | .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .rex }, |
| 288 | .{ .mov, .mr, .rm16, .r16, .none, .none, &.{ 0x89 }, 0, .none }, | 306 | .{ .mov, .mr, .rm16, .r16, .none, .none, &.{ 0x89 }, 0, .none }, |
| ... | @@ -316,6 +334,15 @@ pub const table = &[_]Entry{ | ... | @@ -316,6 +334,15 @@ pub const table = &[_]Entry{ |
| 316 | .{ .mov, .mi, .rm32, .imm32, .none, .none, &.{ 0xc7 }, 0, .none }, | 334 | .{ .mov, .mi, .rm32, .imm32, .none, .none, &.{ 0xc7 }, 0, .none }, |
| 317 | .{ .mov, .mi, .rm64, .imm32s, .none, .none, &.{ 0xc7 }, 0, .long }, | 335 | .{ .mov, .mi, .rm64, .imm32s, .none, .none, &.{ 0xc7 }, 0, .long }, |
| 318 | 336 | ||
| 337 | .{ .movs, .np, .m8, .m8, .none, .none, &.{ 0xa4 }, 0, .none }, | ||
| 338 | .{ .movs, .np, .m16, .m16, .none, .none, &.{ 0xa5 }, 0, .none }, | ||
| 339 | .{ .movs, .np, .m32, .m32, .none, .none, &.{ 0xa5 }, 0, .none }, | ||
| 340 | .{ .movs, .np, .m64, .m64, .none, .none, &.{ 0xa5 }, 0, .long }, | ||
| 341 | .{ .movsb, .np, .none, .none, .none, .none, &.{ 0xa4 }, 0, .none }, | ||
| 342 | .{ .movsw, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .short }, | ||
| 343 | .{ .movsd, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .none }, | ||
| 344 | .{ .movsq, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .long }, | ||
| 345 | |||
| 319 | .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, | 346 | .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, |
| 320 | .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .rex }, | 347 | .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .rex }, |
| 321 | .{ .movsx, .rm, .r32, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, | 348 | .{ .movsx, .rm, .r32, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, |
| ... | @@ -435,6 +462,15 @@ pub const table = &[_]Entry{ | ... | @@ -435,6 +462,15 @@ pub const table = &[_]Entry{ |
| 435 | .{ .sbb, .rm, .r32, .rm32, .none, .none, &.{ 0x1b }, 0, .none }, | 462 | .{ .sbb, .rm, .r32, .rm32, .none, .none, &.{ 0x1b }, 0, .none }, |
| 436 | .{ .sbb, .rm, .r64, .rm64, .none, .none, &.{ 0x1b }, 0, .long }, | 463 | .{ .sbb, .rm, .r64, .rm64, .none, .none, &.{ 0x1b }, 0, .long }, |
| 437 | 464 | ||
| 465 | .{ .scas, .np, .m8, .none, .none, .none, &.{ 0xae }, 0, .none }, | ||
| 466 | .{ .scas, .np, .m16, .none, .none, .none, &.{ 0xaf }, 0, .none }, | ||
| 467 | .{ .scas, .np, .m32, .none, .none, .none, &.{ 0xaf }, 0, .none }, | ||
| 468 | .{ .scas, .np, .m64, .none, .none, .none, &.{ 0xaf }, 0, .long }, | ||
| 469 | .{ .scasb, .np, .none, .none, .none, .none, &.{ 0xae }, 0, .none }, | ||
| 470 | .{ .scasw, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .short }, | ||
| 471 | .{ .scasd, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .none }, | ||
| 472 | .{ .scasq, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .long }, | ||
| 473 | |||
| 438 | .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .none }, | 474 | .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .none }, |
| 439 | .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .rex }, | 475 | .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .rex }, |
| 440 | .{ .setae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .none }, | 476 | .{ .setae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .none }, |
| ... | @@ -528,6 +564,15 @@ pub const table = &[_]Entry{ | ... | @@ -528,6 +564,15 @@ pub const table = &[_]Entry{ |
| 528 | .{ .shr, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 5, .none }, | 564 | .{ .shr, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 5, .none }, |
| 529 | .{ .shr, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 5, .long }, | 565 | .{ .shr, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 5, .long }, |
| 530 | 566 | ||
| 567 | .{ .stos, .np, .m8, .none, .none, .none, &.{ 0xaa }, 0, .none }, | ||
| 568 | .{ .stos, .np, .m16, .none, .none, .none, &.{ 0xab }, 0, .none }, | ||
| 569 | .{ .stos, .np, .m32, .none, .none, .none, &.{ 0xab }, 0, .none }, | ||
| 570 | .{ .stos, .np, .m64, .none, .none, .none, &.{ 0xab }, 0, .long }, | ||
| 571 | .{ .stosb, .np, .none, .none, .none, .none, &.{ 0xaa }, 0, .none }, | ||
| 572 | .{ .stosw, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .short }, | ||
| 573 | .{ .stosd, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .none }, | ||
| 574 | .{ .stosq, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .long }, | ||
| 575 | |||
| 531 | .{ .sub, .zi, .al, .imm8, .none, .none, &.{ 0x2c }, 0, .none }, | 576 | .{ .sub, .zi, .al, .imm8, .none, .none, &.{ 0x2c }, 0, .none }, |
| 532 | .{ .sub, .zi, .ax, .imm16, .none, .none, &.{ 0x2d }, 0, .none }, | 577 | .{ .sub, .zi, .ax, .imm16, .none, .none, &.{ 0x2d }, 0, .none }, |
| 533 | .{ .sub, .zi, .eax, .imm32, .none, .none, &.{ 0x2d }, 0, .none }, | 578 | .{ .sub, .zi, .eax, .imm32, .none, .none, &.{ 0x2d }, 0, .none }, |