authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-20 19:38:15+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-20 23:29:41+01:00
log5156ccd552fa6d0959c8349cbbe176099ac4c356
tree62a76e97732ce29f9c46b460859b134c0bdc47ce
parent71c5eebd32d44ca01fedcb48674c5e7185f10289

stage2: merge MOV back with arith instructions

* turns out MOV and other arithmetic instructions such as ADD can naturally share the same lowering codepath (for the same variants) * there are variants that are specific to ADD, or MOV which will be implemented as standalone MIR tags * tweak Isel tests to generate corresponding test cases for all arithmetic instructions in comptime

3 files changed, 478 insertions(+), 587 deletions(-)

src/arch/x86_64/CodeGen.zig+8-7
...@@ -1608,18 +1608,18 @@ fn genBinMathOpMir(...@@ -1608,18 +1608,18 @@ fn genBinMathOpMir(
1608 _ = try self.addInst(.{1608 _ = try self.addInst(.{
1609 .tag = mir_tag,1609 .tag = mir_tag,
1610 .ops = (Mir.Ops{1610 .ops = (Mir.Ops{
1611 .reg1 = src_reg,1611 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
1612 .reg2 = dst_reg,1612 .reg2 = src_reg,
1613 .flags = 0b11,
1614 }).encode(),1613 }).encode(),
1615 .data = undefined,1614 .data = undefined,
1616 });1615 });
1617 },1616 },
1618 .immediate => |imm| {1617 .immediate => |imm| {
1618 // TODO I am not quite sure why we need to set the size of the register here...
1619 _ = try self.addInst(.{1619 _ = try self.addInst(.{
1620 .tag = mir_tag,1620 .tag = mir_tag,
1621 .ops = (Mir.Ops{1621 .ops = (Mir.Ops{
1622 .reg1 = dst_reg,1622 .reg1 = registerAlias(dst_reg, 4),
1623 }).encode(),1623 }).encode(),
1624 .data = .{ .imm = @intCast(i32, imm) },1624 .data = .{ .imm = @intCast(i32, imm) },
1625 });1625 });
...@@ -1637,7 +1637,7 @@ fn genBinMathOpMir(...@@ -1637,7 +1637,7 @@ fn genBinMathOpMir(
1637 .tag = mir_tag,1637 .tag = mir_tag,
1638 .ops = (Mir.Ops{1638 .ops = (Mir.Ops{
1639 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),1639 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
1640 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),1640 .reg2 = .rbp,
1641 .flags = 0b01,1641 .flags = 0b01,
1642 }).encode(),1642 }).encode(),
1643 .data = .{ .imm = -@intCast(i32, adj_off) },1643 .data = .{ .imm = -@intCast(i32, adj_off) },
...@@ -1667,8 +1667,8 @@ fn genBinMathOpMir(...@@ -1667,8 +1667,8 @@ fn genBinMathOpMir(
1667 _ = try self.addInst(.{1667 _ = try self.addInst(.{
1668 .tag = mir_tag,1668 .tag = mir_tag,
1669 .ops = (Mir.Ops{1669 .ops = (Mir.Ops{
1670 .reg1 = registerAlias(src_reg, @intCast(u32, abi_size)),1670 .reg1 = .rbp,
1671 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),1671 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
1672 .flags = 0b10,1672 .flags = 0b10,
1673 }).encode(),1673 }).encode(),
1674 .data = .{ .imm = -@intCast(i32, adj_off) },1674 .data = .{ .imm = -@intCast(i32, adj_off) },
...@@ -2924,6 +2924,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2924,6 +2924,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2924 }2924 }
2925 if (x <= math.maxInt(i32)) {2925 if (x <= math.maxInt(i32)) {
2926 // Next best case: if we set the lower four bytes, the upper four will be zeroed.2926 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
2927 // TODO I am not quite sure why we need to set the size of the register here...
2927 _ = try self.addInst(.{2928 _ = try self.addInst(.{
2928 .tag = .mov,2929 .tag = .mov,
2929 .ops = (Mir.Ops{2930 .ops = (Mir.Ops{
src/arch/x86_64/Emit.zig+470-567
...@@ -93,46 +93,41 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -93,46 +93,41 @@ pub fn emitMir(emit: *Emit) InnerError!void {
93 const inst = @intCast(u32, index);93 const inst = @intCast(u32, index);
94 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);94 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
95 switch (tag) {95 switch (tag) {
96 .adc => try emit.mirArith(.adc, inst),96 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try emit.mirArith(tag, inst),
97 .add => try emit.mirArith(.add, inst),97
98 .sub => try emit.mirArith(.sub, inst),98 .adc_scale_src,
99 .xor => try emit.mirArith(.xor, inst),99 .add_scale_src,
100 .@"and" => try emit.mirArith(.@"and", inst),100 .sub_scale_src,
101 .@"or" => try emit.mirArith(.@"or", inst),101 .xor_scale_src,
102 .sbb => try emit.mirArith(.sbb, inst),102 .and_scale_src,
103 .cmp => try emit.mirArith(.cmp, inst),103 .or_scale_src,
104104 .sbb_scale_src,
105 .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),105 .cmp_scale_src,
106 .add_scale_src => try emit.mirArithScaleSrc(.add, inst),106 .mov_scale_src,
107 .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst),107 => try emit.mirArithScaleSrc(tag, inst),
108 .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst),108
109 .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst),109 .adc_scale_dst,
110 .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),110 .add_scale_dst,
111 .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),111 .sub_scale_dst,
112 .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),112 .xor_scale_dst,
113113 .and_scale_dst,
114 .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),114 .or_scale_dst,
115 .add_scale_dst => try emit.mirArithScaleDst(.add, inst),115 .sbb_scale_dst,
116 .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst),116 .cmp_scale_dst,
117 .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst),117 .mov_scale_dst,
118 .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst),118 => try emit.mirArithScaleDst(tag, inst),
119 .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),119
120 .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst),120 .adc_scale_imm,
121 .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst),121 .add_scale_imm,
122122 .sub_scale_imm,
123 .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst),123 .xor_scale_imm,
124 .add_scale_imm => try emit.mirArithScaleImm(.add, inst),124 .and_scale_imm,
125 .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst),125 .or_scale_imm,
126 .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst),126 .sbb_scale_imm,
127 .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst),127 .cmp_scale_imm,
128 .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst),128 .mov_scale_imm,
129 .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst),129 => try emit.mirArithScaleImm(tag, inst),
130 .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst),130
131
132 .mov => try emit.mirMov(inst),
133 .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),
134 .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst),
135 .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst),
136 .movabs => try emit.mirMovabs(inst),131 .movabs => try emit.mirMovabs(inst),
137132
138 .lea => try emit.mirLea(inst),133 .lea => try emit.mirLea(inst),
...@@ -140,19 +135,19 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -140,19 +135,19 @@ pub fn emitMir(emit: *Emit) InnerError!void {
140135
141 .imul_complex => try emit.mirIMulComplex(inst),136 .imul_complex => try emit.mirIMulComplex(inst),
142137
143 .push => try emit.mirPushPop(.push, inst),138 .push, .pop => try emit.mirPushPop(tag, inst),
144 .pop => try emit.mirPushPop(.pop, inst),
145139
146 .jmp => try emit.mirJmpCall(.jmp, inst),140 .jmp, .call => try emit.mirJmpCall(tag, inst),
147 .call => try emit.mirJmpCall(.call, inst),
148141
149 .cond_jmp_greater_less => try emit.mirCondJmp(.cond_jmp_greater_less, inst),142 .cond_jmp_greater_less,
150 .cond_jmp_above_below => try emit.mirCondJmp(.cond_jmp_above_below, inst),143 .cond_jmp_above_below,
151 .cond_jmp_eq_ne => try emit.mirCondJmp(.cond_jmp_eq_ne, inst),144 .cond_jmp_eq_ne,
145 => try emit.mirCondJmp(tag, inst),
152146
153 .cond_set_byte_greater_less => try emit.mirCondSetByte(.cond_set_byte_greater_less, inst),147 .cond_set_byte_greater_less,
154 .cond_set_byte_above_below => try emit.mirCondSetByte(.cond_set_byte_above_below, inst),148 .cond_set_byte_above_below,
155 .cond_set_byte_eq_ne => try emit.mirCondSetByte(.cond_set_byte_eq_ne, inst),149 .cond_set_byte_eq_ne,
150 => try emit.mirCondSetByte(tag, inst),
156151
157 .ret => try emit.mirRet(inst),152 .ret => try emit.mirRet(inst),
158153
...@@ -598,6 +593,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {...@@ -598,6 +593,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
598 .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },593 .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },
599 .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },594 .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },
600 .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },595 .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },
596 .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 },
601 else => unreachable,597 else => unreachable,
602 },598 },
603 .mr => {599 .mr => {
...@@ -610,6 +606,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {...@@ -610,6 +606,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
610 .@"or" => 0x09,606 .@"or" => 0x09,
611 .sbb => 0x19,607 .sbb => 0x19,
612 .cmp => 0x39,608 .cmp => 0x39,
609 .mov => 0x89,
613 else => unreachable,610 else => unreachable,
614 };611 };
615 return .{ .opc = opc, .modrm_ext = undefined };612 return .{ .opc = opc, .modrm_ext = undefined };
...@@ -624,6 +621,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {...@@ -624,6 +621,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
624 .@"or" => 0x0b,621 .@"or" => 0x0b,
625 .sbb => 0x1b,622 .sbb => 0x1b,
626 .cmp => 0x3b,623 .cmp => 0x3b,
624 .mov => 0x8b,
627 else => unreachable,625 else => unreachable,
628 };626 };
629 return .{ .opc = opc, .modrm_ext = undefined };627 return .{ .opc = opc, .modrm_ext = undefined };
...@@ -632,262 +630,9 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {...@@ -632,262 +630,9 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
632}630}
633631
634fn mirArith(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {632fn mirArith(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
635 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);633 const res = try mirArithImpl(
636 switch (ops.flags) {
637 0b00 => blk: {
638 if (ops.reg2 == .none) {
639 // OP reg1, imm32
640 // OP r/m64, imm32
641 const imm = emit.mir.instructions.items(.data)[inst].imm;
642 const opcode = getArithOpCode(tag, .mi);
643 const encoder = try Encoder.init(emit.code, 7);
644 encoder.rex(.{
645 .w = ops.reg1.size() == 64,
646 .b = ops.reg1.isExtended(),
647 });
648 if (tag != .mov and imm <= math.maxInt(i8)) {
649 encoder.opcode_1byte(opcode.opc + 2);
650 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
651 encoder.imm8(@intCast(i8, imm));
652 } else {
653 encoder.opcode_1byte(opcode.opc);
654 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
655 encoder.imm32(imm);
656 }
657 break :blk;
658 }
659 // OP reg1, reg2
660 // OP r/m64, r64
661 const opcode = getArithOpCode(tag, .mr);
662 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
663 const encoder = try Encoder.init(emit.code, 3);
664 encoder.rex(.{
665 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
666 .r = ops.reg1.isExtended(),
667 .b = ops.reg2.isExtended(),
668 });
669 encoder.opcode_1byte(opc);
670 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
671 },
672 0b01 => blk: {
673 const imm = emit.mir.instructions.items(.data)[inst].imm;
674 const opcode = getArithOpCode(tag, .rm);
675 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
676 if (ops.reg2 == .none) {
677 // OP reg1, [imm32]
678 // OP r64, r/m64
679 const encoder = try Encoder.init(emit.code, 8);
680 encoder.rex(.{
681 .w = ops.reg1.size() == 64,
682 .b = ops.reg1.isExtended(),
683 });
684 encoder.opcode_1byte(opc);
685 encoder.modRm_SIBDisp0(ops.reg1.lowId());
686 encoder.sib_disp32();
687 encoder.disp32(imm);
688 break :blk;
689 }
690 // OP reg1, [reg2 + imm32]
691 // OP r64, r/m64
692 const encoder = try Encoder.init(emit.code, 7);
693 encoder.rex(.{
694 .w = ops.reg1.size() == 64,
695 .r = ops.reg1.isExtended(),
696 .b = ops.reg2.isExtended(),
697 });
698 encoder.opcode_1byte(opc);
699 if (imm <= math.maxInt(i8)) {
700 encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId());
701 encoder.disp8(@intCast(i8, imm));
702 } else {
703 encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId());
704 encoder.disp32(imm);
705 }
706 },
707 0b10 => blk: {
708 if (ops.reg2 == .none) {
709 // OP [reg1 + 0], imm32
710 // OP r/m64, imm32
711 const imm = emit.mir.instructions.items(.data)[inst].imm;
712 const opcode = getArithOpCode(tag, .mi);
713 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
714 const encoder = try Encoder.init(emit.code, 7);
715 encoder.rex(.{
716 .w = ops.reg1.size() == 64,
717 .b = ops.reg1.isExtended(),
718 });
719 encoder.opcode_1byte(opc);
720 encoder.modRm_indirectDisp0(opcode.modrm_ext, ops.reg1.lowId());
721 if (imm <= math.maxInt(i8)) {
722 encoder.imm8(@intCast(i8, imm));
723 } else if (imm <= math.maxInt(i16)) {
724 encoder.imm16(@intCast(i16, imm));
725 } else {
726 encoder.imm32(imm);
727 }
728 break :blk;
729 }
730 // OP [reg1 + imm32], reg2
731 // OP r/m64, r64
732 const imm = emit.mir.instructions.items(.data)[inst].imm;
733 const opcode = getArithOpCode(tag, .mr);
734 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
735 const encoder = try Encoder.init(emit.code, 7);
736 encoder.rex(.{
737 .w = ops.reg2.size() == 64,
738 .r = ops.reg1.isExtended(),
739 .b = ops.reg2.isExtended(),
740 });
741 encoder.opcode_1byte(opc);
742 if (imm <= math.maxInt(i8)) {
743 encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId());
744 encoder.disp8(@intCast(i8, imm));
745 } else {
746 encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId());
747 encoder.disp32(imm);
748 }
749 },
750 0b11 => blk: {
751 if (ops.reg2 == .none) {
752 // OP [reg1 + imm32], imm32
753 // OP r/m64, imm32
754 const payload = emit.mir.instructions.items(.data)[inst].payload;
755 const imm_pair = Mir.extraData(emit.mir.extra, Mir.ImmPair, payload).data;
756 const opcode = getArithOpCode(tag, .mi);
757 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
758 const encoder = try Encoder.init(emit.code, 11);
759 encoder.rex(.{
760 .w = false,
761 .b = ops.reg1.isExtended(),
762 });
763 encoder.opcode_1byte(opc);
764 if (imm_pair.dest_off <= math.maxInt(i8)) {
765 encoder.modRm_indirectDisp8(opcode.modrm_ext, ops.reg1.lowId());
766 encoder.disp8(@intCast(i8, imm_pair.dest_off));
767 } else {
768 encoder.modRm_indirectDisp32(opcode.modrm_ext, ops.reg1.lowId());
769 encoder.disp32(imm_pair.dest_off);
770 }
771 encoder.imm32(imm_pair.operand);
772 break :blk;
773 }
774 // TODO clearly mov doesn't belong here; for other, arithemtic ops,
775 // this is the same as 0b00.
776 const opcode = getArithOpCode(tag, if (tag == .mov) .rm else .mr);
777 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
778 const encoder = try Encoder.init(emit.code, 3);
779 encoder.rex(.{
780 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
781 .r = ops.reg1.isExtended(),
782 .b = ops.reg2.isExtended(),
783 });
784 encoder.opcode_1byte(opc);
785 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
786 },
787 }
788}
789
790fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
791 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
792 const scale = ops.flags;
793 // OP reg1, [reg2 + scale*rcx + imm32]
794 const opcode = getArithOpCode(tag, .rm);
795 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
796 const imm = emit.mir.instructions.items(.data)[inst].imm;
797 const encoder = try Encoder.init(emit.code, 8);
798 encoder.rex(.{
799 .w = ops.reg1.size() == 64,
800 .r = ops.reg1.isExtended(),
801 .b = ops.reg2.isExtended(),
802 });
803 encoder.opcode_1byte(opc);
804 if (imm <= math.maxInt(i8)) {
805 encoder.modRm_SIBDisp8(ops.reg1.lowId());
806 encoder.sib_scaleIndexBaseDisp8(scale, Register.rcx.lowId(), ops.reg2.lowId());
807 encoder.disp8(@intCast(i8, imm));
808 } else {
809 encoder.modRm_SIBDisp32(ops.reg1.lowId());
810 encoder.sib_scaleIndexBaseDisp32(scale, Register.rcx.lowId(), ops.reg2.lowId());
811 encoder.disp32(imm);
812 }
813}
814
815fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
816 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
817 const scale = ops.flags;
818 const imm = emit.mir.instructions.items(.data)[inst].imm;
819
820 if (ops.reg2 == .none) {
821 // OP [reg1 + scale*rax + 0], imm32
822 const opcode = getArithOpCode(tag, .mi);
823 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
824 const encoder = try Encoder.init(emit.code, 8);
825 encoder.rex(.{
826 .w = ops.reg1.size() == 64,
827 .b = ops.reg1.isExtended(),
828 });
829 encoder.opcode_1byte(opc);
830 encoder.modRm_SIBDisp0(opcode.modrm_ext);
831 encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId());
832 if (imm <= math.maxInt(i8)) {
833 encoder.imm8(@intCast(i8, imm));
834 } else if (imm <= math.maxInt(i16)) {
835 encoder.imm16(@intCast(i16, imm));
836 } else {
837 encoder.imm32(imm);
838 }
839 return;
840 }
841
842 // OP [reg1 + scale*rax + imm32], reg2
843 const opcode = getArithOpCode(tag, .mr);
844 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
845 const encoder = try Encoder.init(emit.code, 8);
846 encoder.rex(.{
847 .w = ops.reg1.size() == 64,
848 .r = ops.reg2.isExtended(),
849 .b = ops.reg1.isExtended(),
850 });
851 encoder.opcode_1byte(opc);
852 if (imm <= math.maxInt(i8)) {
853 encoder.modRm_SIBDisp8(ops.reg2.lowId());
854 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
855 encoder.disp8(@intCast(i8, imm));
856 } else {
857 encoder.modRm_SIBDisp32(ops.reg2.lowId());
858 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
859 encoder.disp32(imm);
860 }
861}
862
863fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
864 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
865 const scale = ops.flags;
866 const payload = emit.mir.instructions.items(.data)[inst].payload;
867 const imm_pair = Mir.extraData(emit.mir.extra, Mir.ImmPair, payload).data;
868 const opcode = getArithOpCode(tag, .mi);
869 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
870 const encoder = try Encoder.init(emit.code, 2);
871 encoder.rex(.{
872 .w = ops.reg1.size() == 64,
873 .b = ops.reg1.isExtended(),
874 });
875 encoder.opcode_1byte(opc);
876 if (imm_pair.dest_off <= math.maxInt(i8)) {
877 encoder.modRm_SIBDisp8(opcode.modrm_ext);
878 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
879 encoder.disp8(@intCast(i8, imm_pair.dest_off));
880 } else {
881 encoder.modRm_SIBDisp32(opcode.modrm_ext);
882 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
883 encoder.disp32(imm_pair.dest_off);
884 }
885 encoder.imm32(imm_pair.operand);
886}
887
888fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
889 const res = try mirMovImpl(
890 emit.bin_file.allocator,634 emit.bin_file.allocator,
635 tag,
891 emit.mir.instructions,636 emit.mir.instructions,
892 emit.mir.extra,637 emit.mir.extra,
893 inst,638 inst,
...@@ -900,8 +645,9 @@ fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -900,8 +645,9 @@ fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
900 }645 }
901}646}
902647
903fn mirMovImpl(648fn mirArithImpl(
904 allocator: Allocator,649 allocator: Allocator,
650 tag: Mir.Inst.Tag,
905 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,651 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,
906 mir_extra: []const u32,652 mir_extra: []const u32,
907 inst: Mir.Inst.Index,653 inst: Mir.Inst.Index,
...@@ -915,8 +661,8 @@ fn mirMovImpl(...@@ -915,8 +661,8 @@ fn mirMovImpl(
915 // mov reg1, imm32661 // mov reg1, imm32
916 // MI662 // MI
917 const imm = mir_instructions.items(.data)[inst].imm;663 const imm = mir_instructions.items(.data)[inst].imm;
918 const opc: u8 = if (ops.reg1.size() == 8) 0xc6 else 0xc7;664 const opcode = getArithOpCode(tag, .mi);
919 const modrm_ext: u3 = 0x0;665 const opc: u8 = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
920 const encoder = try Encoder.init(code, 7);666 const encoder = try Encoder.init(code, 7);
921 if (ops.reg1.size() == 16) {667 if (ops.reg1.size() == 16) {
922 // 0x66 prefix switches to the non-default size; here we assume a switch from668 // 0x66 prefix switches to the non-default size; here we assume a switch from
...@@ -929,7 +675,7 @@ fn mirMovImpl(...@@ -929,7 +675,7 @@ fn mirMovImpl(
929 .b = ops.reg1.isExtended(),675 .b = ops.reg1.isExtended(),
930 });676 });
931 encoder.opcode_1byte(opc);677 encoder.opcode_1byte(opc);
932 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());678 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
933 switch (ops.reg1.size()) {679 switch (ops.reg1.size()) {
934 8 => {680 8 => {
935 const imm8 = math.cast(i8, imm) catch {681 const imm8 = math.cast(i8, imm) catch {
...@@ -974,7 +720,8 @@ fn mirMovImpl(...@@ -974,7 +720,8 @@ fn mirMovImpl(
974 ops.reg2,720 ops.reg2,
975 });721 });
976 }722 }
977 const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89;723 const opcode = getArithOpCode(tag, .mr);
724 const opc: u8 = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
978 const encoder = try Encoder.init(code, 3);725 const encoder = try Encoder.init(code, 3);
979 encoder.rex(.{726 encoder.rex(.{
980 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,727 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
...@@ -986,7 +733,8 @@ fn mirMovImpl(...@@ -986,7 +733,8 @@ fn mirMovImpl(
986 },733 },
987 0b01 => blk: {734 0b01 => blk: {
988 const imm = mir_instructions.items(.data)[inst].imm;735 const imm = mir_instructions.items(.data)[inst].imm;
989 const opc: u8 = if (ops.reg1.size() == 8) 0x8a else 0x8b;736 const opcode = getArithOpCode(tag, .rm);
737 const opc: u8 = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
990 if (ops.reg2 == .none) {738 if (ops.reg2 == .none) {
991 // mov reg1, [imm32]739 // mov reg1, [imm32]
992 // RM740 // RM
...@@ -1045,15 +793,14 @@ fn mirMovImpl(...@@ -1045,15 +793,14 @@ fn mirMovImpl(
1045 // a byte, word or dword ptr.793 // a byte, word or dword ptr.
1046 // TODO we currently don't have a way to flag imm32 64bit sign extended794 // TODO we currently don't have a way to flag imm32 64bit sign extended
1047 const imm = mir_instructions.items(.data)[inst].imm;795 const imm = mir_instructions.items(.data)[inst].imm;
1048 const opc: u8 = 0xc7;796 const opcode = getArithOpCode(tag, .mi);
1049 const modrm_ext: u3 = 0x0;
1050 const encoder = try Encoder.init(code, 7);797 const encoder = try Encoder.init(code, 7);
1051 encoder.rex(.{798 encoder.rex(.{
1052 .w = false,799 .w = false,
1053 .b = ops.reg1.isExtended(),800 .b = ops.reg1.isExtended(),
1054 });801 });
1055 encoder.opcode_1byte(opc);802 encoder.opcode_1byte(opcode.opc);
1056 encoder.modRm_indirectDisp0(modrm_ext, ops.reg1.lowId());803 encoder.modRm_indirectDisp0(opcode.modrm_ext, ops.reg1.lowId());
1057 encoder.imm32(imm);804 encoder.imm32(imm);
1058 break :blk;805 break :blk;
1059 }806 }
...@@ -1066,7 +813,8 @@ fn mirMovImpl(...@@ -1066,7 +813,8 @@ fn mirMovImpl(
1066 // * reg2 is 16bit - word ptr813 // * reg2 is 16bit - word ptr
1067 // * reg2 is 8bit - byte ptr814 // * reg2 is 8bit - byte ptr
1068 const imm = mir_instructions.items(.data)[inst].imm;815 const imm = mir_instructions.items(.data)[inst].imm;
1069 const opc: u8 = if (ops.reg2.size() == 8) 0x88 else 0x89;816 const opcode = getArithOpCode(tag, .mr);
817 const opc: u8 = if (ops.reg2.size() == 8) opcode.opc - 1 else opcode.opc;
1070 const encoder = try Encoder.init(code, 5);818 const encoder = try Encoder.init(code, 5);
1071 if (ops.reg2.size() == 16) {819 if (ops.reg2.size() == 16) {
1072 encoder.opcode_1byte(0x66);820 encoder.opcode_1byte(0x66);
...@@ -1101,19 +849,18 @@ fn mirMovImpl(...@@ -1101,19 +849,18 @@ fn mirMovImpl(
1101 const payload = mir_instructions.items(.data)[inst].payload;849 const payload = mir_instructions.items(.data)[inst].payload;
1102 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;850 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;
1103 const imm_op = imm_pair.operand;851 const imm_op = imm_pair.operand;
1104 const opc: u8 = 0xc7;852 const opcode = getArithOpCode(tag, .mi);
1105 const modrm_ext: u3 = 0x0;
1106 const encoder = try Encoder.init(code, 10);853 const encoder = try Encoder.init(code, 10);
1107 encoder.rex(.{854 encoder.rex(.{
1108 .w = false,855 .w = false,
1109 .b = ops.reg1.isExtended(),856 .b = ops.reg1.isExtended(),
1110 });857 });
1111 encoder.opcode_1byte(opc);858 encoder.opcode_1byte(opcode.opc);
1112 if (immOpSize(imm_pair.dest_off) == 8) {859 if (immOpSize(imm_pair.dest_off) == 8) {
1113 encoder.modRm_indirectDisp8(modrm_ext, ops.reg1.lowId());860 encoder.modRm_indirectDisp8(opcode.modrm_ext, ops.reg1.lowId());
1114 encoder.disp8(@intCast(i8, imm_pair.dest_off));861 encoder.disp8(@intCast(i8, imm_pair.dest_off));
1115 } else {862 } else {
1116 encoder.modRm_indirectDisp32(modrm_ext, ops.reg1.lowId());863 encoder.modRm_indirectDisp32(opcode.modrm_ext, ops.reg1.lowId());
1117 encoder.disp32(imm_pair.dest_off);864 encoder.disp32(imm_pair.dest_off);
1118 }865 }
1119 encoder.imm32(imm_op);866 encoder.imm32(imm_op);
...@@ -1137,6 +884,104 @@ fn immOpSize(imm: i32) u8 {...@@ -1137,6 +884,104 @@ fn immOpSize(imm: i32) u8 {
1137 return 32;884 return 32;
1138}885}
1139886
887fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
888 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
889 const scale = ops.flags;
890 // OP reg1, [reg2 + scale*rcx + imm32]
891 const opcode = getArithOpCode(tag, .rm);
892 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
893 const imm = emit.mir.instructions.items(.data)[inst].imm;
894 const encoder = try Encoder.init(emit.code, 8);
895 encoder.rex(.{
896 .w = ops.reg1.size() == 64,
897 .r = ops.reg1.isExtended(),
898 .b = ops.reg2.isExtended(),
899 });
900 encoder.opcode_1byte(opc);
901 if (imm <= math.maxInt(i8)) {
902 encoder.modRm_SIBDisp8(ops.reg1.lowId());
903 encoder.sib_scaleIndexBaseDisp8(scale, Register.rcx.lowId(), ops.reg2.lowId());
904 encoder.disp8(@intCast(i8, imm));
905 } else {
906 encoder.modRm_SIBDisp32(ops.reg1.lowId());
907 encoder.sib_scaleIndexBaseDisp32(scale, Register.rcx.lowId(), ops.reg2.lowId());
908 encoder.disp32(imm);
909 }
910}
911
912fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
913 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
914 const scale = ops.flags;
915 const imm = emit.mir.instructions.items(.data)[inst].imm;
916
917 if (ops.reg2 == .none) {
918 // OP [reg1 + scale*rax + 0], imm32
919 const opcode = getArithOpCode(tag, .mi);
920 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
921 const encoder = try Encoder.init(emit.code, 8);
922 encoder.rex(.{
923 .w = ops.reg1.size() == 64,
924 .b = ops.reg1.isExtended(),
925 });
926 encoder.opcode_1byte(opc);
927 encoder.modRm_SIBDisp0(opcode.modrm_ext);
928 encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId());
929 if (imm <= math.maxInt(i8)) {
930 encoder.imm8(@intCast(i8, imm));
931 } else if (imm <= math.maxInt(i16)) {
932 encoder.imm16(@intCast(i16, imm));
933 } else {
934 encoder.imm32(imm);
935 }
936 return;
937 }
938
939 // OP [reg1 + scale*rax + imm32], reg2
940 const opcode = getArithOpCode(tag, .mr);
941 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
942 const encoder = try Encoder.init(emit.code, 8);
943 encoder.rex(.{
944 .w = ops.reg1.size() == 64,
945 .r = ops.reg2.isExtended(),
946 .b = ops.reg1.isExtended(),
947 });
948 encoder.opcode_1byte(opc);
949 if (imm <= math.maxInt(i8)) {
950 encoder.modRm_SIBDisp8(ops.reg2.lowId());
951 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
952 encoder.disp8(@intCast(i8, imm));
953 } else {
954 encoder.modRm_SIBDisp32(ops.reg2.lowId());
955 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
956 encoder.disp32(imm);
957 }
958}
959
960fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
961 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
962 const scale = ops.flags;
963 const payload = emit.mir.instructions.items(.data)[inst].payload;
964 const imm_pair = Mir.extraData(emit.mir.extra, Mir.ImmPair, payload).data;
965 const opcode = getArithOpCode(tag, .mi);
966 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
967 const encoder = try Encoder.init(emit.code, 2);
968 encoder.rex(.{
969 .w = ops.reg1.size() == 64,
970 .b = ops.reg1.isExtended(),
971 });
972 encoder.opcode_1byte(opc);
973 if (imm_pair.dest_off <= math.maxInt(i8)) {
974 encoder.modRm_SIBDisp8(opcode.modrm_ext);
975 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
976 encoder.disp8(@intCast(i8, imm_pair.dest_off));
977 } else {
978 encoder.modRm_SIBDisp32(opcode.modrm_ext);
979 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
980 encoder.disp32(imm_pair.dest_off);
981 }
982 encoder.imm32(imm_pair.operand);
983}
984
1140fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {985fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1141 const tag = emit.mir.instructions.items(.tag)[inst];986 const tag = emit.mir.instructions.items(.tag)[inst];
1142 assert(tag == .movabs);987 assert(tag == .movabs);
...@@ -1552,8 +1397,9 @@ const Mock = struct {...@@ -1552,8 +1397,9 @@ const Mock = struct {
1552 const code_index = self.code.items.len;1397 const code_index = self.code.items.len;
1553 const mir_index = try self.addInst(mir_inst);1398 const mir_index = try self.addInst(mir_inst);
1554 const res = switch (mir_inst.tag) {1399 const res = switch (mir_inst.tag) {
1555 .mov => try mirMovImpl(1400 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl(
1556 testing.allocator,1401 testing.allocator,
1402 mir_inst.tag,
1557 self.mir_instructions.slice(),1403 self.mir_instructions.slice(),
1558 self.mir_extra.items,1404 self.mir_extra.items,
1559 mir_index,1405 mir_index,
...@@ -1575,8 +1421,9 @@ const Mock = struct {...@@ -1575,8 +1421,9 @@ const Mock = struct {
1575 const dummy_src_loc = Mock.dummySrcLoc();1421 const dummy_src_loc = Mock.dummySrcLoc();
1576 const index = try self.addInst(mir_inst);1422 const index = try self.addInst(mir_inst);
1577 const res = switch (mir_inst.tag) {1423 const res = switch (mir_inst.tag) {
1578 .mov => try mirMovImpl(1424 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl(
1579 testing.allocator,1425 testing.allocator,
1426 mir_inst.tag,
1580 self.mir_instructions.slice(),1427 self.mir_instructions.slice(),
1581 self.mir_extra.items,1428 self.mir_extra.items,
1582 index,1429 index,
...@@ -1611,278 +1458,334 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []co...@@ -1611,278 +1458,334 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []co
1611 return error.TestFailed;1458 return error.TestFailed;
1612}1459}
16131460
1614test "mov dst_reg, src_reg" {1461test "ARITH_OP/MOV dst_reg, src_reg" {
1615 var mock = Mock.init();1462 var mock = Mock.init();
1616 defer mock.deinit();1463 defer mock.deinit();
1617 try mock.testEmitSingleSuccess(.{1464 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1618 .tag = .mov,1465 const opcode = comptime getArithOpCode(tag, .mr);
1619 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .rsp }).encode(),1466 const opc = [1]u8{opcode.opc};
1620 .data = undefined,1467 const opc_1 = [1]u8{opcode.opc - 1};
1621 }, "\x48\x89\xe5", "mov rbp, rsp");1468 try mock.testEmitSingleSuccess(.{
1622 try mock.testEmitSingleSuccess(.{1469 .tag = tag,
1623 .tag = .mov,1470 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .rsp }).encode(),
1624 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),1471 .data = undefined,
1625 .data = undefined,1472 }, "\x48" ++ opc ++ "\xe5", @tagName(tag) ++ " rbp, rsp");
1626 }, "\x49\x89\xc4", "mov r12, rax");1473 try mock.testEmitSingleSuccess(.{
1627 try mock.testEmitSingleFail(.{1474 .tag = tag,
1628 .tag = .mov,1475 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
1629 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),1476 .data = undefined,
1630 .data = undefined,1477 }, "\x49" ++ opc ++ "\xc4", @tagName(tag) ++ " r12, rax");
1631 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");1478 try mock.testEmitSingleFail(.{
1632 try mock.testEmitSingleFail(.{1479 .tag = tag,
1633 .tag = .mov,1480 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
1634 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),1481 .data = undefined,
1635 .data = undefined,1482 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
1636 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");1483 try mock.testEmitSingleFail(.{
1637 try mock.testEmitSingleSuccess(.{1484 .tag = tag,
1638 .tag = .mov,1485 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
1639 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),1486 .data = undefined,
1640 .data = undefined,1487 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
1641 }, "\x41\x89\xc4", "mov r12d, eax");1488 try mock.testEmitSingleSuccess(.{
16421489 .tag = tag,
1643 // TODO mov r12b, ah requires a codepath without REX prefix1490 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
1644 try mock.testEmitSingleSuccess(.{1491 .data = undefined,
1645 .tag = .mov,1492 }, "\x41" ++ opc ++ "\xc4", @tagName(tag) ++ " r12d, eax");
1646 .ops = (Mir.Ops{ .reg1 = .r12b, .reg2 = .al }).encode(),1493 // TODO mov r12b, ah requires a codepath without REX prefix
1647 .data = undefined,1494 try mock.testEmitSingleSuccess(.{
1648 }, "\x41\x88\xc4", "mov r12b, al");1495 .tag = tag,
1496 .ops = (Mir.Ops{ .reg1 = .r12b, .reg2 = .al }).encode(),
1497 .data = undefined,
1498 }, "\x41" ++ opc_1 ++ "\xc4", @tagName(tag) ++ " r12b, al");
1499 }
1649}1500}
16501501
1651test "mov dst_reg, imm" {1502test "ARITH_OP/MOV dst_reg, imm" {
1652 var mock = Mock.init();1503 var mock = Mock.init();
1653 defer mock.deinit();1504 defer mock.deinit();
1654 try mock.testEmitSingleSuccess(.{
1655 .tag = .mov,
1656 .ops = (Mir.Ops{ .reg1 = .rcx }).encode(),
1657 .data = .{ .imm = 0x10 },
1658 }, "\x48\xc7\xc1\x10\x00\x00\x00", "mov rcx, 0x10");
1659
1660 // TODO we are wasting one byte here: this could be encoded as OI with the encoding opc + rd, imm8/16/32
1661 // b9 10 00 00 00
1662 try mock.testEmitSingleSuccess(.{
1663 .tag = .mov,
1664 .ops = (Mir.Ops{ .reg1 = .ecx }).encode(),
1665 .data = .{ .imm = 0x10 },
1666 }, "\xc7\xc1\x10\x00\x00\x00", "mov ecx, 0x10");
1667 try mock.testEmitSingleSuccess(.{
1668 .tag = .mov,
1669 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
1670 .data = .{ .imm = 0x10 },
1671 }, "\x66\xc7\xc1\x10\x00", "mov cx, 0x10");
1672 try mock.testEmitSingleSuccess(.{
1673 .tag = .mov,
1674 .ops = (Mir.Ops{ .reg1 = .r11w }).encode(),
1675 .data = .{ .imm = 0x10 },
1676 }, "\x66\x41\xC7\xC3\x10\x00", "mov r11w, 0x10");
1677 try mock.testEmitSingleSuccess(.{
1678 .tag = .mov,
1679 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1680 .data = .{ .imm = 0x10 },
1681 }, "\xc6\xc1\x10", "mov cl, 0x10");
1682 try mock.testEmitSingleFail(.{
1683 .tag = .mov,
1684 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
1685 .data = .{ .imm = 0x10000000 },
1686 }, "size mismatch: sizeof Register.cx != sizeof 0x10000000");
1687 try mock.testEmitSingleFail(.{
1688 .tag = .mov,
1689 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1690 .data = .{ .imm = 0x1000 },
1691 }, "size mismatch: sizeof Register.cl != sizeof 0x1000");
1692}
16931505
1694test "mov dst_reg, [imm32]" {1506 const ModRmByte = struct {
1695 var mock = Mock.init();1507 inline fn get(tag: Mir.Inst.Tag, reg: u8) [1]u8 {
1696 defer mock.deinit();1508 const modrm: u8 = getArithOpCode(tag, .mi).modrm_ext;
1697 try mock.testEmitSingleSuccess(.{1509 return .{0xc0 + (modrm << 3) + reg};
1698 .tag = .mov,1510 }
1699 .ops = (Mir.Ops{ .reg1 = .rcx, .flags = 0b01 }).encode(),1511 };
1700 .data = .{ .imm = 0x10 },1512
1701 }, "\x48\x8B\x0C\x25\x10\x00\x00\x00", "mov rcx, [0x10]");1513 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1702 try mock.testEmitSingleSuccess(.{1514 const opcode = comptime getArithOpCode(tag, .mi);
1703 .tag = .mov,1515 const opc = [1]u8{opcode.opc};
1704 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b01 }).encode(),1516 const opc_1 = [1]u8{opcode.opc - 1};
1705 .data = .{ .imm = 0x10 },1517 try mock.testEmitSingleSuccess(.{
1706 }, "\x4C\x8B\x1C\x25\x10\x00\x00\x00", "mov r11, [0x10]");1518 .tag = tag,
1707 try mock.testEmitSingleSuccess(.{1519 .ops = (Mir.Ops{ .reg1 = .rcx }).encode(),
1708 .tag = .mov,1520 .data = .{ .imm = 0x10 },
1709 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b01 }).encode(),1521 }, "\x48" ++ opc ++ ModRmByte.get(tag, 1) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " rcx, 0x10");
1710 .data = .{ .imm = 0x10 },1522 // TODO we are wasting one byte here: this could be encoded as OI with the encoding
1711 }, "\x44\x8B\x1C\x25\x10\x00\x00\x00", "mov r11d, [0x10]");1523 // opc + rd, imm8/16/32
1712 try mock.testEmitSingleSuccess(.{1524 // b9 10 00 00 00
1713 .tag = .mov,1525 try mock.testEmitSingleSuccess(.{
1714 .ops = (Mir.Ops{ .reg1 = .r11w, .flags = 0b01 }).encode(),1526 .tag = tag,
1715 .data = .{ .imm = 0x10 },1527 .ops = (Mir.Ops{ .reg1 = .ecx }).encode(),
1716 }, "\x66\x44\x8B\x1C\x25\x10\x00\x00\x00", "mov r11w, [0x10]");1528 .data = .{ .imm = 0x10 },
1717 try mock.testEmitSingleSuccess(.{1529 }, opc ++ ModRmByte.get(tag, 1) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " ecx, 0x10");
1718 .tag = .mov,1530 try mock.testEmitSingleSuccess(.{
1719 .ops = (Mir.Ops{ .reg1 = .r11b, .flags = 0b01 }).encode(),1531 .tag = tag,
1720 .data = .{ .imm = 0x10 },1532 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
1721 }, "\x44\x8A\x1C\x25\x10\x00\x00\x00", "mov r11b, [0x10]");1533 .data = .{ .imm = 0x10 },
1534 }, "\x66" ++ opc ++ ModRmByte.get(tag, 1) ++ "\x10\x00", @tagName(tag) ++ " cx, 0x10");
1535 try mock.testEmitSingleSuccess(.{
1536 .tag = tag,
1537 .ops = (Mir.Ops{ .reg1 = .r11w }).encode(),
1538 .data = .{ .imm = 0x10 },
1539 }, "\x66\x41" ++ opc ++ ModRmByte.get(tag, 3) ++ "\x10\x00", @tagName(tag) ++ " r11w, 0x10");
1540 try mock.testEmitSingleSuccess(.{
1541 .tag = tag,
1542 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1543 .data = .{ .imm = 0x10 },
1544 }, opc_1 ++ ModRmByte.get(tag, 1) ++ "\x10", @tagName(tag) ++ " cl, 0x10");
1545 try mock.testEmitSingleFail(.{
1546 .tag = .mov,
1547 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
1548 .data = .{ .imm = 0x10000000 },
1549 }, "size mismatch: sizeof Register.cx != sizeof 0x10000000");
1550 try mock.testEmitSingleFail(.{
1551 .tag = .mov,
1552 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1553 .data = .{ .imm = 0x1000 },
1554 }, "size mismatch: sizeof Register.cl != sizeof 0x1000");
1555 }
1722}1556}
17231557
1724test "mov dst_reg, [src_reg + imm]" {1558test "ARITH_OP/MOV dst_reg, [imm32]" {
1725 var mock = Mock.init();1559 var mock = Mock.init();
1726 defer mock.deinit();1560 defer mock.deinit();
1727 try mock.testEmitSingleSuccess(.{1561 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1728 .tag = .mov,1562 const opcode = comptime getArithOpCode(tag, .rm);
1729 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),1563 const opc = [1]u8{opcode.opc};
1730 .data = .{ .imm = 0x10 },1564 const opc_1 = [1]u8{opcode.opc - 1};
1731 }, "\x48\x8B\x4D\x10", "mov rcx, [rbp + 0x10]");1565 try mock.testEmitSingleSuccess(.{
1732 try mock.testEmitSingleSuccess(.{1566 .tag = tag,
1733 .tag = .mov,1567 .ops = (Mir.Ops{ .reg1 = .rcx, .flags = 0b01 }).encode(),
1734 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),1568 .data = .{ .imm = 0x10 },
1735 .data = .{ .imm = 0x10000000 },1569 }, "\x48" ++ opc ++ "\x0C\x25\x10\x00\x00\x00", @tagName(tag) ++ " rcx, [0x10]");
1736 }, "\x48\x8B\x8D\x00\x00\x00\x10", "mov rcx, [rbp + 0x10000000]");1570 try mock.testEmitSingleSuccess(.{
1737 try mock.testEmitSingleSuccess(.{1571 .tag = tag,
1738 .tag = .mov,1572 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b01 }).encode(),
1739 .ops = (Mir.Ops{ .reg1 = .r11b, .reg2 = .rbp, .flags = 0b01 }).encode(),1573 .data = .{ .imm = 0x10 },
1740 .data = .{ .imm = 0x10 },1574 }, "\x4C" ++ opc ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11, [0x10]");
1741 }, "\x44\x8A\x5D\x10", "mov r11b, [rbp + 0x10]");1575 try mock.testEmitSingleSuccess(.{
1742 try mock.testEmitSingleSuccess(.{1576 .tag = tag,
1743 .tag = .mov,1577 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b01 }).encode(),
1744 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .rbp, .flags = 0b01 }).encode(),1578 .data = .{ .imm = 0x10 },
1745 .data = .{ .imm = 0x10000000 },1579 }, "\x44" ++ opc ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11d, [0x10]");
1746 }, "\x66\x44\x8B\x9D\x00\x00\x00\x10", "mov r11w, [rbp + 0x10000000]");1580 try mock.testEmitSingleSuccess(.{
1581 .tag = tag,
1582 .ops = (Mir.Ops{ .reg1 = .r11w, .flags = 0b01 }).encode(),
1583 .data = .{ .imm = 0x10 },
1584 }, "\x66\x44" ++ opc ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11w, [0x10]");
1585 try mock.testEmitSingleSuccess(.{
1586 .tag = tag,
1587 .ops = (Mir.Ops{ .reg1 = .r11b, .flags = 0b01 }).encode(),
1588 .data = .{ .imm = 0x10 },
1589 }, "\x44" ++ opc_1 ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11b, [0x10]");
1590 }
1747}1591}
17481592
1749test "mov [dst_reg + 0], imm" {1593test "ARITH_OP/MOV dst_reg, [src_reg + imm]" {
1750 var mock = Mock.init();1594 var mock = Mock.init();
1751 defer mock.deinit();1595 defer mock.deinit();
1752 try mock.testEmitSingleSuccess(.{1596 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1753 .tag = .mov,1597 const opcode = comptime getArithOpCode(tag, .rm);
1754 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b10 }).encode(),1598 const opc = [1]u8{opcode.opc};
1755 .data = .{ .imm = 0x10 },1599 const opc_1 = [1]u8{opcode.opc - 1};
1756 }, "\x41\xC7\x03\x10\x00\x00\x00", "mov dword ptr [r11 + 0], 0x10");1600 try mock.testEmitSingleSuccess(.{
1757 try mock.testEmitSingleSuccess(.{1601 .tag = tag,
1758 .tag = .mov,1602 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1759 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),1603 .data = .{ .imm = 0x10 },
1760 .data = .{ .imm = 0x10000000 },1604 }, "\x48" ++ opc ++ "\x4D\x10", @tagName(tag) ++ " rcx, [rbp + 0x10]");
1761 }, "\xC7\x00\x00\x00\x00\x10", "mov dword ptr [rax + 0], 0x10000000");1605 try mock.testEmitSingleSuccess(.{
1762 try mock.testEmitSingleSuccess(.{1606 .tag = tag,
1763 .tag = .mov,1607 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1764 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),1608 .data = .{ .imm = 0x10000000 },
1765 .data = .{ .imm = 0x1000 },1609 }, "\x48" ++ opc ++ "\x8D\x00\x00\x00\x10", @tagName(tag) ++ " rcx, [rbp + 0x10000000]");
1766 }, "\xC7\x00\x00\x10\x00\x00", "mov dword ptr [rax + 0], 0x1000");1610 try mock.testEmitSingleSuccess(.{
1767 try mock.testEmitSingleSuccess(.{1611 .tag = tag,
1768 .tag = .mov,1612 .ops = (Mir.Ops{ .reg1 = .r11b, .reg2 = .rbp, .flags = 0b01 }).encode(),
1769 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),1613 .data = .{ .imm = 0x10 },
1770 .data = .{ .imm = 0x10 },1614 }, "\x44" ++ opc_1 ++ "\x5D\x10", @tagName(tag) ++ " r11b, [rbp + 0x10]");
1771 }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");1615 try mock.testEmitSingleSuccess(.{
1772 try mock.testEmitSingleFail(.{1616 .tag = tag,
1773 .tag = .mov,1617 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .rbp, .flags = 0b01 }).encode(),
1774 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),1618 .data = .{ .imm = 0x10000000 },
1775 .data = .{ .imm = 0x10 },1619 }, "\x66\x44" ++ opc ++ "\x9D\x00\x00\x00\x10", @tagName(tag) ++ " r11w, [rbp + 0x10000000]");
1776 }, "size mismatch: sizeof Register.eax != 8");1620 }
1777}1621}
17781622
1779test "mov [dst_reg + imm32], src_reg" {1623test "ARITH_OP/MOV [dst_reg + 0], imm" {
1780 var mock = Mock.init();1624 var mock = Mock.init();
1781 defer mock.deinit();1625 defer mock.deinit();
1782 try mock.testEmitSingleSuccess(.{1626
1783 .tag = .mov,1627 const ModRmByte = struct {
1784 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11, .flags = 0b10 }).encode(),1628 inline fn get(tag: Mir.Inst.Tag, reg: u8) [1]u8 {
1785 .data = .{ .imm = 0x10 },1629 const modrm: u8 = getArithOpCode(tag, .mi).modrm_ext;
1786 }, "\x4c\x89\x5d\x10", "mov qword ptr [rbp + 0x10], r11");1630 return .{(modrm << 3) + reg};
1787 try mock.testEmitSingleSuccess(.{1631 }
1788 .tag = .mov,1632 };
1789 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11d, .flags = 0b10 }).encode(),1633
1790 .data = .{ .imm = 0x10 },1634 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1791 }, "\x44\x89\x5d\x10", "mov dword ptr [rbp + 0x10], r11d");1635 const opcode = comptime getArithOpCode(tag, .mi);
1792 try mock.testEmitSingleSuccess(.{1636 const opc = [1]u8{opcode.opc};
1793 .tag = .mov,1637 try mock.testEmitSingleSuccess(.{
1794 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11w, .flags = 0b10 }).encode(),1638 .tag = tag,
1795 .data = .{ .imm = 0x10 },1639 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b10 }).encode(),
1796 }, "\x66\x44\x89\x5d\x10", "mov word ptr [rbp + 0x10], r11w");1640 .data = .{ .imm = 0x10 },
1797 try mock.testEmitSingleSuccess(.{1641 }, "\x41" ++ opc ++ ModRmByte.get(tag, 3) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " dword ptr [r11 + 0], 0x10");
1798 .tag = .mov,1642 try mock.testEmitSingleSuccess(.{
1799 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11b, .flags = 0b10 }).encode(),1643 .tag = tag,
1800 .data = .{ .imm = 0x10 },1644 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1801 }, "\x44\x88\x5d\x10", "mov byte ptr [rbp + 0x10], r11b");1645 .data = .{ .imm = 0x10000000 },
1802 try mock.testEmitSingleSuccess(.{1646 }, opc ++ ModRmByte.get(tag, 0) ++ "\x00\x00\x00\x10", @tagName(tag) ++ " dword ptr [rax + 0], 0x10000000");
1803 .tag = .mov,1647 try mock.testEmitSingleSuccess(.{
1804 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .rax, .flags = 0b10 }).encode(),1648 .tag = tag,
1805 .data = .{ .imm = 0x10 },1649 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1806 }, "\x49\x89\x43\x10", "mov qword ptr [r11 + 0x10], rax");1650 .data = .{ .imm = 0x1000 },
1807 try mock.testEmitSingleSuccess(.{1651 }, opc ++ ModRmByte.get(tag, 0) ++ "\x00\x10\x00\x00", @tagName(tag) ++ " dword ptr [rax + 0], 0x1000");
1808 .tag = .mov,1652 try mock.testEmitSingleSuccess(.{
1809 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),1653 .tag = tag,
1810 .data = .{ .imm = 0x10 },1654 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1811 }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");1655 .data = .{ .imm = 0x10 },
1812 try mock.testEmitSingleFail(.{1656 }, opc ++ ModRmByte.get(tag, 0) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " dword ptr [rax + 0], 0x10");
1813 .tag = .mov,1657 try mock.testEmitSingleFail(.{
1814 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),1658 .tag = tag,
1815 .data = .{ .imm = 0x10 },1659 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
1816 }, "size mismatch: sizeof Register.r11w != 8");1660 .data = .{ .imm = 0x10 },
1661 }, "size mismatch: sizeof Register.eax != 8");
1662 }
1817}1663}
18181664
1819test "mov [dst_reg + imm32], imm32" {1665test "ARITH_OP/MOV [dst_reg + imm32], src_reg" {
1820 var mock = Mock.init();1666 var mock = Mock.init();
1821 defer mock.deinit();1667 defer mock.deinit();
1822 {1668 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1823 const payload = try mock.addExtra(Mir.ImmPair{1669 const opcode = comptime getArithOpCode(tag, .mr);
1824 .dest_off = 0x10,1670 const opc = [1]u8{opcode.opc};
1825 .operand = 0x20000000,1671 const opc_1 = [1]u8{opcode.opc - 1};
1826 });
1827 try mock.testEmitSingleSuccess(.{1672 try mock.testEmitSingleSuccess(.{
1828 .tag = .mov,1673 .tag = tag,
1829 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),1674 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11, .flags = 0b10 }).encode(),
1830 .data = .{ .payload = payload },1675 .data = .{ .imm = 0x10 },
1831 }, "\xC7\x45\x10\x00\x00\x00\x20", "mov dword ptr [rbp + 0x10], 0x20000000");1676 }, "\x4c" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " qword ptr [rbp + 0x10], r11");
1832 }
1833 {
1834 const payload = try mock.addExtra(Mir.ImmPair{
1835 .dest_off = 0x10,
1836 .operand = 0x2000,
1837 });
1838 try mock.testEmitSingleSuccess(.{1677 try mock.testEmitSingleSuccess(.{
1839 .tag = .mov,1678 .tag = tag,
1840 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),1679 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11d, .flags = 0b10 }).encode(),
1841 .data = .{ .payload = payload },1680 .data = .{ .imm = 0x10 },
1842 }, "\xC7\x45\x10\x00\x20\x00\x00", "mov dword ptr [rbp + 0x10], 0x2000");1681 }, "\x44" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " dword ptr [rbp + 0x10], r11d");
1843 }
1844 {
1845 const payload = try mock.addExtra(Mir.ImmPair{
1846 .dest_off = 0x10,
1847 .operand = 0x20,
1848 });
1849 try mock.testEmitSingleSuccess(.{1682 try mock.testEmitSingleSuccess(.{
1850 .tag = .mov,1683 .tag = tag,
1851 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),1684 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11w, .flags = 0b10 }).encode(),
1852 .data = .{ .payload = payload },1685 .data = .{ .imm = 0x10 },
1853 }, "\xc7\x45\x10\x20\x00\x00\x00", "mov dword ptr [rbp + 0x10], 0x20");1686 }, "\x66\x44" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " word ptr [rbp + 0x10], r11w");
1854 }
1855 {
1856 const payload = try mock.addExtra(Mir.ImmPair{
1857 .dest_off = 0x10,
1858 .operand = 0x20000000,
1859 });
1860 try mock.testEmitSingleSuccess(.{1687 try mock.testEmitSingleSuccess(.{
1861 .tag = .mov,1688 .tag = tag,
1862 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),1689 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11b, .flags = 0b10 }).encode(),
1863 .data = .{ .payload = payload },1690 .data = .{ .imm = 0x10 },
1864 }, "\x41\xC7\x43\x10\x00\x00\x00\x20", "mov dword ptr [r11 + 0x10], 0x20000000");1691 }, "\x44" ++ opc_1 ++ "\x5d\x10", @tagName(tag) ++ " byte ptr [rbp + 0x10], r11b");
1865 }
1866 {
1867 const payload = try mock.addExtra(Mir.ImmPair{
1868 .dest_off = 0x10000000,
1869 .operand = 0x20000000,
1870 });
1871 try mock.testEmitSingleSuccess(.{1692 try mock.testEmitSingleSuccess(.{
1872 .tag = .mov,1693 .tag = tag,
1873 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),1694 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .rax, .flags = 0b10 }).encode(),
1874 .data = .{ .payload = payload },1695 .data = .{ .imm = 0x10 },
1875 }, "\x41\xC7\x83\x00\x00\x00\x10\x00\x00\x00\x20", "mov dword ptr [r11 + 0x10], 0x20000000");1696 }, "\x49" ++ opc ++ "\x43\x10", @tagName(tag) ++ " qword ptr [r11 + 0x10], rax");
1876 }1697 try mock.testEmitSingleSuccess(.{
1877 {1698 .tag = tag,
1878 const payload = try mock.addExtra(Mir.ImmPair{1699 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
1879 .dest_off = 0x10,1700 .data = .{ .imm = 0x10 },
1880 .operand = 0x20,1701 }, "\x41" ++ opc ++ "\x43\x10", @tagName(tag) ++ " dword ptr [r11 + 0x10], eax");
1881 });
1882 try mock.testEmitSingleFail(.{1702 try mock.testEmitSingleFail(.{
1883 .tag = .mov,1703 .tag = tag,
1884 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),1704 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
1885 .data = .{ .payload = payload },1705 .data = .{ .imm = 0x10 },
1886 }, "size mismatch: sizeof Register.r11d != 8");1706 }, "size mismatch: sizeof Register.r11w != 8");
1707 }
1708}
1709
1710test "ARITH_OP/MOV [dst_reg + imm32], imm32" {
1711 var mock = Mock.init();
1712 defer mock.deinit();
1713
1714 const ModRmByte = struct {
1715 inline fn get(tag: Mir.Inst.Tag, disp: u2, reg: u8) [1]u8 {
1716 const modrm: u8 = getArithOpCode(tag, .mi).modrm_ext;
1717 return .{(@as(u8, disp) << 6) + (modrm << 3) + reg};
1718 }
1719 };
1720
1721 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1722 const opcode = comptime getArithOpCode(tag, .mi);
1723 const opc = [1]u8{opcode.opc};
1724 {
1725 const payload = try mock.addExtra(Mir.ImmPair{
1726 .dest_off = 0x10,
1727 .operand = 0x20000000,
1728 });
1729 try mock.testEmitSingleSuccess(.{
1730 .tag = tag,
1731 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1732 .data = .{ .payload = payload },
1733 }, opc ++ ModRmByte.get(tag, 1, 5) ++ "\x10\x00\x00\x00\x20", @tagName(tag) ++ " dword ptr [rbp + 0x10], 0x20000000");
1734 }
1735 {
1736 const payload = try mock.addExtra(Mir.ImmPair{
1737 .dest_off = 0x10,
1738 .operand = 0x2000,
1739 });
1740 try mock.testEmitSingleSuccess(.{
1741 .tag = tag,
1742 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1743 .data = .{ .payload = payload },
1744 }, opc ++ ModRmByte.get(tag, 1, 5) ++ "\x10\x00\x20\x00\x00", @tagName(tag) ++ " dword ptr [rbp + 0x10], 0x2000");
1745 }
1746 {
1747 const payload = try mock.addExtra(Mir.ImmPair{
1748 .dest_off = 0x10,
1749 .operand = 0x20,
1750 });
1751 try mock.testEmitSingleSuccess(.{
1752 .tag = tag,
1753 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1754 .data = .{ .payload = payload },
1755 }, opc ++ ModRmByte.get(tag, 1, 5) ++ "\x10\x20\x00\x00\x00", @tagName(tag) ++ " dword ptr [rbp + 0x10], 0x20");
1756 }
1757 {
1758 const payload = try mock.addExtra(Mir.ImmPair{
1759 .dest_off = 0x10,
1760 .operand = 0x20000000,
1761 });
1762 try mock.testEmitSingleSuccess(.{
1763 .tag = tag,
1764 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),
1765 .data = .{ .payload = payload },
1766 }, "\x41" ++ opc ++ ModRmByte.get(tag, 1, 3) ++ "\x10\x00\x00\x00\x20", @tagName(tag) ++ " dword ptr [r11 + 0x10], 0x20000000");
1767 }
1768 {
1769 const payload = try mock.addExtra(Mir.ImmPair{
1770 .dest_off = 0x10000000,
1771 .operand = 0x20000000,
1772 });
1773 try mock.testEmitSingleSuccess(.{
1774 .tag = tag,
1775 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),
1776 .data = .{ .payload = payload },
1777 }, "\x41" ++ opc ++ ModRmByte.get(tag, 2, 3) ++ "\x00\x00\x00\x10\x00\x00\x00\x20", @tagName(tag) ++ " dword ptr [r11 + 0x10], 0x20000000");
1778 }
1779 {
1780 const payload = try mock.addExtra(Mir.ImmPair{
1781 .dest_off = 0x10,
1782 .operand = 0x20,
1783 });
1784 try mock.testEmitSingleFail(.{
1785 .tag = tag,
1786 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),
1787 .data = .{ .payload = payload },
1788 }, "size mismatch: sizeof Register.r11d != 8");
1789 }
1887 }1790 }
1888}1791}
src/arch/x86_64/Mir.zig-13
...@@ -136,19 +136,6 @@ pub const Inst = struct {...@@ -136,19 +136,6 @@ pub const Inst = struct {
136 cmp_scale_src,136 cmp_scale_src,
137 cmp_scale_dst,137 cmp_scale_dst,
138 cmp_scale_imm,138 cmp_scale_imm,
139
140 /// ops flags: form:
141 /// 0b00 reg1, reg2 (MR)
142 /// 0b00 reg1, imm32
143 /// 0b01 reg1, [reg2 + imm32]
144 /// 0b01 reg1, [ds:imm32]
145 /// 0b10 [reg1 + imm32], reg2
146 /// 0b10 [reg1 + 0], imm32
147 /// 0b11 [reg1 + imm32], imm32
148 /// 0b11 AVAILABLE
149 /// Notes:
150 /// * If reg2 is `none` then it means Data field `imm` is used as the immediate.
151 /// * When two imm32 values are required, Data field `payload` points at `ImmPair`.
152 mov,139 mov,
153 mov_scale_src,140 mov_scale_src,
154 mov_scale_dst,141 mov_scale_dst,