authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-18 05:49:20-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-21 08:49:54+01:00
logedd63f9abaa7dae1786a4cd91cc3031264bd3ef0
tree2259ac328fe3d045d0a5ce318b55b611cff7da81
parentc865c8fb2a910e73e38192a8bd484935bae8f6fc

x86_64: reimplement inline memcpy and memset


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}
12881288
1289pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [count]Register) !void {1289pub 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 }
15421542
1543 try self.spillRegisters(2, .{ .rax, .rdx });1543 try self.spillRegisters(&.{ .rax, .rdx });
15441544
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();
15951595
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 }
15991599
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;
17231723
1724 try self.spillRegisters(2, .{ .rax, .rdx });1724 try self.spillRegisters(&.{ .rax, .rdx });
17251725
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 });
17781778
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 }
18901890
1891 try self.spillRegisters(1, .{.rcx});1891 try self.spillRegisters(&.{.rcx});
18921892
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);
40364037
4037 try self.spillEflagsIfOccupied();4038 try self.spillEflagsIfOccupied();
4039 try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*));
40384040
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 }
40424071
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});
40504080
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);
40554084
...@@ -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);
40634092
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);
56095625
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();
56165627
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(
56385649
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 }
56605671
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}
56935679
5694fn genInlineMemset(5680fn 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 else5689 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);
57125692
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();
57165694
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 }
57385716
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);
57415719 _ = 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}
57735725
5774fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {5726fn 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-bit5758 // 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 {
61366087
6137fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {6088fn 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}
61416092
6142fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {6093fn 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 {
61776128
6178 try self.genInlineMemset(dst_ptr, src_val, len, .{});6129 try self.genInlineMemset(dst_ptr, src_val, len, .{});
61796130
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}
61826133
6183fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {6134fn 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 {
62296180
6230 try self.genInlineMemcpy(dst_ptr, src, len, .{});6181 try self.genInlineMemcpy(dst_ptr, src, len, .{});
62316182
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}
62346185
6235fn airTagName(self: *Self, inst: Air.Inst.Index) !void {6186fn 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),
132132
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),
134141
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}
185192
186fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: struct {193fn 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}
200197
...@@ -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}
320317
318fn 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
321fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {340fn 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}
378397
379fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic {398fn 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}
385403
386fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {404fn 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,
24modrm_ext: u3,24modrm_ext: u3,
25mode: Mode,25mode: Mode,
2626
27pub fn findByMnemonic(mnemonic: Mnemonic, args: struct {27pub 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];
110105
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 }
142133
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}
229215
230pub fn operandBitSize(encoding: Encoding) u64 {216pub 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: off307 // zig fmt: off
318 // General-purpose308 // 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 // SSE2351 // 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 {
591590
592pub const Mode = enum {591pub 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 values150 /// Unordered compare scalar double-precision floating-point values
151 ucomisd,151 ucomisd,
152152
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 move164 /// Conditional move
154 cmovcc,165 cmovcc,
155 /// Conditional jump166 /// 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 source400 /// * `atom_index` is the index of the source
358 /// * `sym_index` is the index of the target401 /// * `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;
6const ArrayList = std.ArrayList;6const ArrayList = std.ArrayList;
7const DW = std.dwarf;7const DW = std.dwarf;
88
9pub const StringRepeat = enum(u3) { none, rep, repe, repz, repne, repnz };
10pub const StringWidth = enum(u2) { b, w, d, q };
11
9/// EFLAGS condition codes12/// EFLAGS condition codes
10pub const Condition = enum(u5) {13pub const Condition = enum(u5) {
11 /// above14 /// 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,
1920
20 pub const Mnemonic = Encoding.Mnemonic;21 pub const Mnemonic = Encoding.Mnemonic;
2122
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 };
98109
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 }
129139
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;
216227
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,
813832
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}
14491464
1450fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: struct {1465fn 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}
14641469
...@@ -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}
14811486
1482fn cannotEncode(mnemonic: Instruction.Mnemonic, args: struct {1487fn 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}
14951490
1496test "cannot encode" {1491test "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 },
209209
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 },
285294
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 },
318336
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 },
437464
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 },
530566
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 },