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(
16081608 _ = try self.addInst(.{
16091609 .tag = mir_tag,
16101610 .ops = (Mir.Ops{
1611 .reg1 = src_reg,
1612 .reg2 = dst_reg,
1613 .flags = 0b11,
1611 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
1612 .reg2 = src_reg,
16141613 }).encode(),
16151614 .data = undefined,
16161615 });
16171616 },
16181617 .immediate => |imm| {
1618 // TODO I am not quite sure why we need to set the size of the register here...
16191619 _ = try self.addInst(.{
16201620 .tag = mir_tag,
16211621 .ops = (Mir.Ops{
1622 .reg1 = dst_reg,
1622 .reg1 = registerAlias(dst_reg, 4),
16231623 }).encode(),
16241624 .data = .{ .imm = @intCast(i32, imm) },
16251625 });
......@@ -1637,7 +1637,7 @@ fn genBinMathOpMir(
16371637 .tag = mir_tag,
16381638 .ops = (Mir.Ops{
16391639 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
1640 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
1640 .reg2 = .rbp,
16411641 .flags = 0b01,
16421642 }).encode(),
16431643 .data = .{ .imm = -@intCast(i32, adj_off) },
......@@ -1667,8 +1667,8 @@ fn genBinMathOpMir(
16671667 _ = try self.addInst(.{
16681668 .tag = mir_tag,
16691669 .ops = (Mir.Ops{
1670 .reg1 = registerAlias(src_reg, @intCast(u32, abi_size)),
1671 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
1670 .reg1 = .rbp,
1671 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
16721672 .flags = 0b10,
16731673 }).encode(),
16741674 .data = .{ .imm = -@intCast(i32, adj_off) },
......@@ -2924,6 +2924,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
29242924 }
29252925 if (x <= math.maxInt(i32)) {
29262926 // 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...
29272928 _ = try self.addInst(.{
29282929 .tag = .mov,
29292930 .ops = (Mir.Ops{
src/arch/x86_64/Emit.zig+470-567
......@@ -93,46 +93,41 @@ pub fn emitMir(emit: *Emit) InnerError!void {
9393 const inst = @intCast(u32, index);
9494 try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
9595 switch (tag) {
96 .adc => try emit.mirArith(.adc, inst),
97 .add => try emit.mirArith(.add, inst),
98 .sub => try emit.mirArith(.sub, inst),
99 .xor => try emit.mirArith(.xor, inst),
100 .@"and" => try emit.mirArith(.@"and", inst),
101 .@"or" => try emit.mirArith(.@"or", inst),
102 .sbb => try emit.mirArith(.sbb, inst),
103 .cmp => try emit.mirArith(.cmp, inst),
104
105 .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),
106 .add_scale_src => try emit.mirArithScaleSrc(.add, inst),
107 .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst),
108 .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst),
109 .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst),
110 .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),
111 .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),
112 .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),
113
114 .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),
115 .add_scale_dst => try emit.mirArithScaleDst(.add, inst),
116 .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst),
117 .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst),
118 .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst),
119 .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),
120 .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst),
121 .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst),
122
123 .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst),
124 .add_scale_imm => try emit.mirArithScaleImm(.add, inst),
125 .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst),
126 .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst),
127 .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst),
128 .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst),
129 .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst),
130 .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst),
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),
96 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try emit.mirArith(tag, inst),
97
98 .adc_scale_src,
99 .add_scale_src,
100 .sub_scale_src,
101 .xor_scale_src,
102 .and_scale_src,
103 .or_scale_src,
104 .sbb_scale_src,
105 .cmp_scale_src,
106 .mov_scale_src,
107 => try emit.mirArithScaleSrc(tag, inst),
108
109 .adc_scale_dst,
110 .add_scale_dst,
111 .sub_scale_dst,
112 .xor_scale_dst,
113 .and_scale_dst,
114 .or_scale_dst,
115 .sbb_scale_dst,
116 .cmp_scale_dst,
117 .mov_scale_dst,
118 => try emit.mirArithScaleDst(tag, inst),
119
120 .adc_scale_imm,
121 .add_scale_imm,
122 .sub_scale_imm,
123 .xor_scale_imm,
124 .and_scale_imm,
125 .or_scale_imm,
126 .sbb_scale_imm,
127 .cmp_scale_imm,
128 .mov_scale_imm,
129 => try emit.mirArithScaleImm(tag, inst),
130
136131 .movabs => try emit.mirMovabs(inst),
137132
138133 .lea => try emit.mirLea(inst),
......@@ -140,19 +135,19 @@ pub fn emitMir(emit: *Emit) InnerError!void {
140135
141136 .imul_complex => try emit.mirIMulComplex(inst),
142137
143 .push => try emit.mirPushPop(.push, inst),
144 .pop => try emit.mirPushPop(.pop, inst),
138 .push, .pop => try emit.mirPushPop(tag, inst),
145139
146 .jmp => try emit.mirJmpCall(.jmp, inst),
147 .call => try emit.mirJmpCall(.call, inst),
140 .jmp, .call => try emit.mirJmpCall(tag, inst),
148141
149 .cond_jmp_greater_less => try emit.mirCondJmp(.cond_jmp_greater_less, inst),
150 .cond_jmp_above_below => try emit.mirCondJmp(.cond_jmp_above_below, inst),
151 .cond_jmp_eq_ne => try emit.mirCondJmp(.cond_jmp_eq_ne, inst),
142 .cond_jmp_greater_less,
143 .cond_jmp_above_below,
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),
154 .cond_set_byte_above_below => try emit.mirCondSetByte(.cond_set_byte_above_below, inst),
155 .cond_set_byte_eq_ne => try emit.mirCondSetByte(.cond_set_byte_eq_ne, inst),
147 .cond_set_byte_greater_less,
148 .cond_set_byte_above_below,
149 .cond_set_byte_eq_ne,
150 => try emit.mirCondSetByte(tag, inst),
156151
157152 .ret => try emit.mirRet(inst),
158153
......@@ -598,6 +593,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
598593 .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },
599594 .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },
600595 .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },
596 .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 },
601597 else => unreachable,
602598 },
603599 .mr => {
......@@ -610,6 +606,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
610606 .@"or" => 0x09,
611607 .sbb => 0x19,
612608 .cmp => 0x39,
609 .mov => 0x89,
613610 else => unreachable,
614611 };
615612 return .{ .opc = opc, .modrm_ext = undefined };
......@@ -624,6 +621,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
624621 .@"or" => 0x0b,
625622 .sbb => 0x1b,
626623 .cmp => 0x3b,
624 .mov => 0x8b,
627625 else => unreachable,
628626 };
629627 return .{ .opc = opc, .modrm_ext = undefined };
......@@ -632,262 +630,9 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
632630}
633631
634632fn 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]);
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(
633 const res = try mirArithImpl(
890634 emit.bin_file.allocator,
635 tag,
891636 emit.mir.instructions,
892637 emit.mir.extra,
893638 inst,
......@@ -900,8 +645,9 @@ fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
900645 }
901646}
902647
903fn mirMovImpl(
648fn mirArithImpl(
904649 allocator: Allocator,
650 tag: Mir.Inst.Tag,
905651 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,
906652 mir_extra: []const u32,
907653 inst: Mir.Inst.Index,
......@@ -915,8 +661,8 @@ fn mirMovImpl(
915661 // mov reg1, imm32
916662 // MI
917663 const imm = mir_instructions.items(.data)[inst].imm;
918 const opc: u8 = if (ops.reg1.size() == 8) 0xc6 else 0xc7;
919 const modrm_ext: u3 = 0x0;
664 const opcode = getArithOpCode(tag, .mi);
665 const opc: u8 = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
920666 const encoder = try Encoder.init(code, 7);
921667 if (ops.reg1.size() == 16) {
922668 // 0x66 prefix switches to the non-default size; here we assume a switch from
......@@ -929,7 +675,7 @@ fn mirMovImpl(
929675 .b = ops.reg1.isExtended(),
930676 });
931677 encoder.opcode_1byte(opc);
932 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());
678 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
933679 switch (ops.reg1.size()) {
934680 8 => {
935681 const imm8 = math.cast(i8, imm) catch {
......@@ -974,7 +720,8 @@ fn mirMovImpl(
974720 ops.reg2,
975721 });
976722 }
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;
978725 const encoder = try Encoder.init(code, 3);
979726 encoder.rex(.{
980727 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
......@@ -986,7 +733,8 @@ fn mirMovImpl(
986733 },
987734 0b01 => blk: {
988735 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;
990738 if (ops.reg2 == .none) {
991739 // mov reg1, [imm32]
992740 // RM
......@@ -1045,15 +793,14 @@ fn mirMovImpl(
1045793 // a byte, word or dword ptr.
1046794 // TODO we currently don't have a way to flag imm32 64bit sign extended
1047795 const imm = mir_instructions.items(.data)[inst].imm;
1048 const opc: u8 = 0xc7;
1049 const modrm_ext: u3 = 0x0;
796 const opcode = getArithOpCode(tag, .mi);
1050797 const encoder = try Encoder.init(code, 7);
1051798 encoder.rex(.{
1052799 .w = false,
1053800 .b = ops.reg1.isExtended(),
1054801 });
1055 encoder.opcode_1byte(opc);
1056 encoder.modRm_indirectDisp0(modrm_ext, ops.reg1.lowId());
802 encoder.opcode_1byte(opcode.opc);
803 encoder.modRm_indirectDisp0(opcode.modrm_ext, ops.reg1.lowId());
1057804 encoder.imm32(imm);
1058805 break :blk;
1059806 }
......@@ -1066,7 +813,8 @@ fn mirMovImpl(
1066813 // * reg2 is 16bit - word ptr
1067814 // * reg2 is 8bit - byte ptr
1068815 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;
1070818 const encoder = try Encoder.init(code, 5);
1071819 if (ops.reg2.size() == 16) {
1072820 encoder.opcode_1byte(0x66);
......@@ -1101,19 +849,18 @@ fn mirMovImpl(
1101849 const payload = mir_instructions.items(.data)[inst].payload;
1102850 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;
1103851 const imm_op = imm_pair.operand;
1104 const opc: u8 = 0xc7;
1105 const modrm_ext: u3 = 0x0;
852 const opcode = getArithOpCode(tag, .mi);
1106853 const encoder = try Encoder.init(code, 10);
1107854 encoder.rex(.{
1108855 .w = false,
1109856 .b = ops.reg1.isExtended(),
1110857 });
1111 encoder.opcode_1byte(opc);
858 encoder.opcode_1byte(opcode.opc);
1112859 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());
1114861 encoder.disp8(@intCast(i8, imm_pair.dest_off));
1115862 } else {
1116 encoder.modRm_indirectDisp32(modrm_ext, ops.reg1.lowId());
863 encoder.modRm_indirectDisp32(opcode.modrm_ext, ops.reg1.lowId());
1117864 encoder.disp32(imm_pair.dest_off);
1118865 }
1119866 encoder.imm32(imm_op);
......@@ -1137,6 +884,104 @@ fn immOpSize(imm: i32) u8 {
1137884 return 32;
1138885}
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
1140985fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1141986 const tag = emit.mir.instructions.items(.tag)[inst];
1142987 assert(tag == .movabs);
......@@ -1552,8 +1397,9 @@ const Mock = struct {
15521397 const code_index = self.code.items.len;
15531398 const mir_index = try self.addInst(mir_inst);
15541399 const res = switch (mir_inst.tag) {
1555 .mov => try mirMovImpl(
1400 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl(
15561401 testing.allocator,
1402 mir_inst.tag,
15571403 self.mir_instructions.slice(),
15581404 self.mir_extra.items,
15591405 mir_index,
......@@ -1575,8 +1421,9 @@ const Mock = struct {
15751421 const dummy_src_loc = Mock.dummySrcLoc();
15761422 const index = try self.addInst(mir_inst);
15771423 const res = switch (mir_inst.tag) {
1578 .mov => try mirMovImpl(
1424 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl(
15791425 testing.allocator,
1426 mir_inst.tag,
15801427 self.mir_instructions.slice(),
15811428 self.mir_extra.items,
15821429 index,
......@@ -1611,278 +1458,334 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []co
16111458 return error.TestFailed;
16121459}
16131460
1614test "mov dst_reg, src_reg" {
1461test "ARITH_OP/MOV dst_reg, src_reg" {
16151462 var mock = Mock.init();
16161463 defer mock.deinit();
1617 try mock.testEmitSingleSuccess(.{
1618 .tag = .mov,
1619 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .rsp }).encode(),
1620 .data = undefined,
1621 }, "\x48\x89\xe5", "mov rbp, rsp");
1622 try mock.testEmitSingleSuccess(.{
1623 .tag = .mov,
1624 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
1625 .data = undefined,
1626 }, "\x49\x89\xc4", "mov r12, rax");
1627 try mock.testEmitSingleFail(.{
1628 .tag = .mov,
1629 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
1630 .data = undefined,
1631 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
1632 try mock.testEmitSingleFail(.{
1633 .tag = .mov,
1634 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
1635 .data = undefined,
1636 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
1637 try mock.testEmitSingleSuccess(.{
1638 .tag = .mov,
1639 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
1640 .data = undefined,
1641 }, "\x41\x89\xc4", "mov r12d, eax");
1642
1643 // TODO mov r12b, ah requires a codepath without REX prefix
1644 try mock.testEmitSingleSuccess(.{
1645 .tag = .mov,
1646 .ops = (Mir.Ops{ .reg1 = .r12b, .reg2 = .al }).encode(),
1647 .data = undefined,
1648 }, "\x41\x88\xc4", "mov r12b, al");
1464 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1465 const opcode = comptime getArithOpCode(tag, .mr);
1466 const opc = [1]u8{opcode.opc};
1467 const opc_1 = [1]u8{opcode.opc - 1};
1468 try mock.testEmitSingleSuccess(.{
1469 .tag = tag,
1470 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .rsp }).encode(),
1471 .data = undefined,
1472 }, "\x48" ++ opc ++ "\xe5", @tagName(tag) ++ " rbp, rsp");
1473 try mock.testEmitSingleSuccess(.{
1474 .tag = tag,
1475 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
1476 .data = undefined,
1477 }, "\x49" ++ opc ++ "\xc4", @tagName(tag) ++ " r12, rax");
1478 try mock.testEmitSingleFail(.{
1479 .tag = tag,
1480 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
1481 .data = undefined,
1482 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
1483 try mock.testEmitSingleFail(.{
1484 .tag = tag,
1485 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
1486 .data = undefined,
1487 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
1488 try mock.testEmitSingleSuccess(.{
1489 .tag = tag,
1490 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
1491 .data = undefined,
1492 }, "\x41" ++ opc ++ "\xc4", @tagName(tag) ++ " r12d, eax");
1493 // TODO mov r12b, ah requires a codepath without REX prefix
1494 try mock.testEmitSingleSuccess(.{
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 }
16491500}
16501501
1651test "mov dst_reg, imm" {
1502test "ARITH_OP/MOV dst_reg, imm" {
16521503 var mock = Mock.init();
16531504 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]" {
1695 var mock = Mock.init();
1696 defer mock.deinit();
1697 try mock.testEmitSingleSuccess(.{
1698 .tag = .mov,
1699 .ops = (Mir.Ops{ .reg1 = .rcx, .flags = 0b01 }).encode(),
1700 .data = .{ .imm = 0x10 },
1701 }, "\x48\x8B\x0C\x25\x10\x00\x00\x00", "mov rcx, [0x10]");
1702 try mock.testEmitSingleSuccess(.{
1703 .tag = .mov,
1704 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b01 }).encode(),
1705 .data = .{ .imm = 0x10 },
1706 }, "\x4C\x8B\x1C\x25\x10\x00\x00\x00", "mov r11, [0x10]");
1707 try mock.testEmitSingleSuccess(.{
1708 .tag = .mov,
1709 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b01 }).encode(),
1710 .data = .{ .imm = 0x10 },
1711 }, "\x44\x8B\x1C\x25\x10\x00\x00\x00", "mov r11d, [0x10]");
1712 try mock.testEmitSingleSuccess(.{
1713 .tag = .mov,
1714 .ops = (Mir.Ops{ .reg1 = .r11w, .flags = 0b01 }).encode(),
1715 .data = .{ .imm = 0x10 },
1716 }, "\x66\x44\x8B\x1C\x25\x10\x00\x00\x00", "mov r11w, [0x10]");
1717 try mock.testEmitSingleSuccess(.{
1718 .tag = .mov,
1719 .ops = (Mir.Ops{ .reg1 = .r11b, .flags = 0b01 }).encode(),
1720 .data = .{ .imm = 0x10 },
1721 }, "\x44\x8A\x1C\x25\x10\x00\x00\x00", "mov r11b, [0x10]");
1506 const ModRmByte = struct {
1507 inline fn get(tag: Mir.Inst.Tag, reg: u8) [1]u8 {
1508 const modrm: u8 = getArithOpCode(tag, .mi).modrm_ext;
1509 return .{0xc0 + (modrm << 3) + reg};
1510 }
1511 };
1512
1513 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1514 const opcode = comptime getArithOpCode(tag, .mi);
1515 const opc = [1]u8{opcode.opc};
1516 const opc_1 = [1]u8{opcode.opc - 1};
1517 try mock.testEmitSingleSuccess(.{
1518 .tag = tag,
1519 .ops = (Mir.Ops{ .reg1 = .rcx }).encode(),
1520 .data = .{ .imm = 0x10 },
1521 }, "\x48" ++ opc ++ ModRmByte.get(tag, 1) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " rcx, 0x10");
1522 // TODO we are wasting one byte here: this could be encoded as OI with the encoding
1523 // opc + rd, imm8/16/32
1524 // b9 10 00 00 00
1525 try mock.testEmitSingleSuccess(.{
1526 .tag = tag,
1527 .ops = (Mir.Ops{ .reg1 = .ecx }).encode(),
1528 .data = .{ .imm = 0x10 },
1529 }, opc ++ ModRmByte.get(tag, 1) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " ecx, 0x10");
1530 try mock.testEmitSingleSuccess(.{
1531 .tag = tag,
1532 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
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 }
17221556}
17231557
1724test "mov dst_reg, [src_reg + imm]" {
1558test "ARITH_OP/MOV dst_reg, [imm32]" {
17251559 var mock = Mock.init();
17261560 defer mock.deinit();
1727 try mock.testEmitSingleSuccess(.{
1728 .tag = .mov,
1729 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1730 .data = .{ .imm = 0x10 },
1731 }, "\x48\x8B\x4D\x10", "mov rcx, [rbp + 0x10]");
1732 try mock.testEmitSingleSuccess(.{
1733 .tag = .mov,
1734 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1735 .data = .{ .imm = 0x10000000 },
1736 }, "\x48\x8B\x8D\x00\x00\x00\x10", "mov rcx, [rbp + 0x10000000]");
1737 try mock.testEmitSingleSuccess(.{
1738 .tag = .mov,
1739 .ops = (Mir.Ops{ .reg1 = .r11b, .reg2 = .rbp, .flags = 0b01 }).encode(),
1740 .data = .{ .imm = 0x10 },
1741 }, "\x44\x8A\x5D\x10", "mov r11b, [rbp + 0x10]");
1742 try mock.testEmitSingleSuccess(.{
1743 .tag = .mov,
1744 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .rbp, .flags = 0b01 }).encode(),
1745 .data = .{ .imm = 0x10000000 },
1746 }, "\x66\x44\x8B\x9D\x00\x00\x00\x10", "mov r11w, [rbp + 0x10000000]");
1561 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1562 const opcode = comptime getArithOpCode(tag, .rm);
1563 const opc = [1]u8{opcode.opc};
1564 const opc_1 = [1]u8{opcode.opc - 1};
1565 try mock.testEmitSingleSuccess(.{
1566 .tag = tag,
1567 .ops = (Mir.Ops{ .reg1 = .rcx, .flags = 0b01 }).encode(),
1568 .data = .{ .imm = 0x10 },
1569 }, "\x48" ++ opc ++ "\x0C\x25\x10\x00\x00\x00", @tagName(tag) ++ " rcx, [0x10]");
1570 try mock.testEmitSingleSuccess(.{
1571 .tag = tag,
1572 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b01 }).encode(),
1573 .data = .{ .imm = 0x10 },
1574 }, "\x4C" ++ opc ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11, [0x10]");
1575 try mock.testEmitSingleSuccess(.{
1576 .tag = tag,
1577 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b01 }).encode(),
1578 .data = .{ .imm = 0x10 },
1579 }, "\x44" ++ opc ++ "\x1C\x25\x10\x00\x00\x00", @tagName(tag) ++ " r11d, [0x10]");
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 }
17471591}
17481592
1749test "mov [dst_reg + 0], imm" {
1593test "ARITH_OP/MOV dst_reg, [src_reg + imm]" {
17501594 var mock = Mock.init();
17511595 defer mock.deinit();
1752 try mock.testEmitSingleSuccess(.{
1753 .tag = .mov,
1754 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b10 }).encode(),
1755 .data = .{ .imm = 0x10 },
1756 }, "\x41\xC7\x03\x10\x00\x00\x00", "mov dword ptr [r11 + 0], 0x10");
1757 try mock.testEmitSingleSuccess(.{
1758 .tag = .mov,
1759 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1760 .data = .{ .imm = 0x10000000 },
1761 }, "\xC7\x00\x00\x00\x00\x10", "mov dword ptr [rax + 0], 0x10000000");
1762 try mock.testEmitSingleSuccess(.{
1763 .tag = .mov,
1764 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1765 .data = .{ .imm = 0x1000 },
1766 }, "\xC7\x00\x00\x10\x00\x00", "mov dword ptr [rax + 0], 0x1000");
1767 try mock.testEmitSingleSuccess(.{
1768 .tag = .mov,
1769 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1770 .data = .{ .imm = 0x10 },
1771 }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");
1772 try mock.testEmitSingleFail(.{
1773 .tag = .mov,
1774 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
1775 .data = .{ .imm = 0x10 },
1776 }, "size mismatch: sizeof Register.eax != 8");
1596 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1597 const opcode = comptime getArithOpCode(tag, .rm);
1598 const opc = [1]u8{opcode.opc};
1599 const opc_1 = [1]u8{opcode.opc - 1};
1600 try mock.testEmitSingleSuccess(.{
1601 .tag = tag,
1602 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1603 .data = .{ .imm = 0x10 },
1604 }, "\x48" ++ opc ++ "\x4D\x10", @tagName(tag) ++ " rcx, [rbp + 0x10]");
1605 try mock.testEmitSingleSuccess(.{
1606 .tag = tag,
1607 .ops = (Mir.Ops{ .reg1 = .rcx, .reg2 = .rbp, .flags = 0b01 }).encode(),
1608 .data = .{ .imm = 0x10000000 },
1609 }, "\x48" ++ opc ++ "\x8D\x00\x00\x00\x10", @tagName(tag) ++ " rcx, [rbp + 0x10000000]");
1610 try mock.testEmitSingleSuccess(.{
1611 .tag = tag,
1612 .ops = (Mir.Ops{ .reg1 = .r11b, .reg2 = .rbp, .flags = 0b01 }).encode(),
1613 .data = .{ .imm = 0x10 },
1614 }, "\x44" ++ opc_1 ++ "\x5D\x10", @tagName(tag) ++ " r11b, [rbp + 0x10]");
1615 try mock.testEmitSingleSuccess(.{
1616 .tag = tag,
1617 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .rbp, .flags = 0b01 }).encode(),
1618 .data = .{ .imm = 0x10000000 },
1619 }, "\x66\x44" ++ opc ++ "\x9D\x00\x00\x00\x10", @tagName(tag) ++ " r11w, [rbp + 0x10000000]");
1620 }
17771621}
17781622
1779test "mov [dst_reg + imm32], src_reg" {
1623test "ARITH_OP/MOV [dst_reg + 0], imm" {
17801624 var mock = Mock.init();
17811625 defer mock.deinit();
1782 try mock.testEmitSingleSuccess(.{
1783 .tag = .mov,
1784 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11, .flags = 0b10 }).encode(),
1785 .data = .{ .imm = 0x10 },
1786 }, "\x4c\x89\x5d\x10", "mov qword ptr [rbp + 0x10], r11");
1787 try mock.testEmitSingleSuccess(.{
1788 .tag = .mov,
1789 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11d, .flags = 0b10 }).encode(),
1790 .data = .{ .imm = 0x10 },
1791 }, "\x44\x89\x5d\x10", "mov dword ptr [rbp + 0x10], r11d");
1792 try mock.testEmitSingleSuccess(.{
1793 .tag = .mov,
1794 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11w, .flags = 0b10 }).encode(),
1795 .data = .{ .imm = 0x10 },
1796 }, "\x66\x44\x89\x5d\x10", "mov word ptr [rbp + 0x10], r11w");
1797 try mock.testEmitSingleSuccess(.{
1798 .tag = .mov,
1799 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11b, .flags = 0b10 }).encode(),
1800 .data = .{ .imm = 0x10 },
1801 }, "\x44\x88\x5d\x10", "mov byte ptr [rbp + 0x10], r11b");
1802 try mock.testEmitSingleSuccess(.{
1803 .tag = .mov,
1804 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .rax, .flags = 0b10 }).encode(),
1805 .data = .{ .imm = 0x10 },
1806 }, "\x49\x89\x43\x10", "mov qword ptr [r11 + 0x10], rax");
1807 try mock.testEmitSingleSuccess(.{
1808 .tag = .mov,
1809 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
1810 .data = .{ .imm = 0x10 },
1811 }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");
1812 try mock.testEmitSingleFail(.{
1813 .tag = .mov,
1814 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
1815 .data = .{ .imm = 0x10 },
1816 }, "size mismatch: sizeof Register.r11w != 8");
1626
1627 const ModRmByte = struct {
1628 inline fn get(tag: Mir.Inst.Tag, reg: u8) [1]u8 {
1629 const modrm: u8 = getArithOpCode(tag, .mi).modrm_ext;
1630 return .{(modrm << 3) + reg};
1631 }
1632 };
1633
1634 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1635 const opcode = comptime getArithOpCode(tag, .mi);
1636 const opc = [1]u8{opcode.opc};
1637 try mock.testEmitSingleSuccess(.{
1638 .tag = tag,
1639 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b10 }).encode(),
1640 .data = .{ .imm = 0x10 },
1641 }, "\x41" ++ opc ++ ModRmByte.get(tag, 3) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " dword ptr [r11 + 0], 0x10");
1642 try mock.testEmitSingleSuccess(.{
1643 .tag = tag,
1644 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1645 .data = .{ .imm = 0x10000000 },
1646 }, opc ++ ModRmByte.get(tag, 0) ++ "\x00\x00\x00\x10", @tagName(tag) ++ " dword ptr [rax + 0], 0x10000000");
1647 try mock.testEmitSingleSuccess(.{
1648 .tag = tag,
1649 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1650 .data = .{ .imm = 0x1000 },
1651 }, opc ++ ModRmByte.get(tag, 0) ++ "\x00\x10\x00\x00", @tagName(tag) ++ " dword ptr [rax + 0], 0x1000");
1652 try mock.testEmitSingleSuccess(.{
1653 .tag = tag,
1654 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1655 .data = .{ .imm = 0x10 },
1656 }, opc ++ ModRmByte.get(tag, 0) ++ "\x10\x00\x00\x00", @tagName(tag) ++ " dword ptr [rax + 0], 0x10");
1657 try mock.testEmitSingleFail(.{
1658 .tag = tag,
1659 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
1660 .data = .{ .imm = 0x10 },
1661 }, "size mismatch: sizeof Register.eax != 8");
1662 }
18171663}
18181664
1819test "mov [dst_reg + imm32], imm32" {
1665test "ARITH_OP/MOV [dst_reg + imm32], src_reg" {
18201666 var mock = Mock.init();
18211667 defer mock.deinit();
1822 {
1823 const payload = try mock.addExtra(Mir.ImmPair{
1824 .dest_off = 0x10,
1825 .operand = 0x20000000,
1826 });
1668 inline for (&[_]Mir.Inst.Tag{ .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov }) |tag| {
1669 const opcode = comptime getArithOpCode(tag, .mr);
1670 const opc = [1]u8{opcode.opc};
1671 const opc_1 = [1]u8{opcode.opc - 1};
18271672 try mock.testEmitSingleSuccess(.{
1828 .tag = .mov,
1829 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1830 .data = .{ .payload = payload },
1831 }, "\xC7\x45\x10\x00\x00\x00\x20", "mov dword ptr [rbp + 0x10], 0x20000000");
1832 }
1833 {
1834 const payload = try mock.addExtra(Mir.ImmPair{
1835 .dest_off = 0x10,
1836 .operand = 0x2000,
1837 });
1673 .tag = tag,
1674 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11, .flags = 0b10 }).encode(),
1675 .data = .{ .imm = 0x10 },
1676 }, "\x4c" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " qword ptr [rbp + 0x10], r11");
18381677 try mock.testEmitSingleSuccess(.{
1839 .tag = .mov,
1840 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1841 .data = .{ .payload = payload },
1842 }, "\xC7\x45\x10\x00\x20\x00\x00", "mov dword ptr [rbp + 0x10], 0x2000");
1843 }
1844 {
1845 const payload = try mock.addExtra(Mir.ImmPair{
1846 .dest_off = 0x10,
1847 .operand = 0x20,
1848 });
1678 .tag = tag,
1679 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11d, .flags = 0b10 }).encode(),
1680 .data = .{ .imm = 0x10 },
1681 }, "\x44" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " dword ptr [rbp + 0x10], r11d");
18491682 try mock.testEmitSingleSuccess(.{
1850 .tag = .mov,
1851 .ops = (Mir.Ops{ .reg1 = .rbp, .flags = 0b11 }).encode(),
1852 .data = .{ .payload = payload },
1853 }, "\xc7\x45\x10\x20\x00\x00\x00", "mov dword ptr [rbp + 0x10], 0x20");
1854 }
1855 {
1856 const payload = try mock.addExtra(Mir.ImmPair{
1857 .dest_off = 0x10,
1858 .operand = 0x20000000,
1859 });
1683 .tag = tag,
1684 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11w, .flags = 0b10 }).encode(),
1685 .data = .{ .imm = 0x10 },
1686 }, "\x66\x44" ++ opc ++ "\x5d\x10", @tagName(tag) ++ " word ptr [rbp + 0x10], r11w");
18601687 try mock.testEmitSingleSuccess(.{
1861 .tag = .mov,
1862 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),
1863 .data = .{ .payload = payload },
1864 }, "\x41\xC7\x43\x10\x00\x00\x00\x20", "mov dword ptr [r11 + 0x10], 0x20000000");
1865 }
1866 {
1867 const payload = try mock.addExtra(Mir.ImmPair{
1868 .dest_off = 0x10000000,
1869 .operand = 0x20000000,
1870 });
1688 .tag = tag,
1689 .ops = (Mir.Ops{ .reg1 = .rbp, .reg2 = .r11b, .flags = 0b10 }).encode(),
1690 .data = .{ .imm = 0x10 },
1691 }, "\x44" ++ opc_1 ++ "\x5d\x10", @tagName(tag) ++ " byte ptr [rbp + 0x10], r11b");
18711692 try mock.testEmitSingleSuccess(.{
1872 .tag = .mov,
1873 .ops = (Mir.Ops{ .reg1 = .r11, .flags = 0b11 }).encode(),
1874 .data = .{ .payload = payload },
1875 }, "\x41\xC7\x83\x00\x00\x00\x10\x00\x00\x00\x20", "mov dword ptr [r11 + 0x10], 0x20000000");
1876 }
1877 {
1878 const payload = try mock.addExtra(Mir.ImmPair{
1879 .dest_off = 0x10,
1880 .operand = 0x20,
1881 });
1693 .tag = tag,
1694 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .rax, .flags = 0b10 }).encode(),
1695 .data = .{ .imm = 0x10 },
1696 }, "\x49" ++ opc ++ "\x43\x10", @tagName(tag) ++ " qword ptr [r11 + 0x10], rax");
1697 try mock.testEmitSingleSuccess(.{
1698 .tag = tag,
1699 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
1700 .data = .{ .imm = 0x10 },
1701 }, "\x41" ++ opc ++ "\x43\x10", @tagName(tag) ++ " dword ptr [r11 + 0x10], eax");
18821702 try mock.testEmitSingleFail(.{
1883 .tag = .mov,
1884 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),
1885 .data = .{ .payload = payload },
1886 }, "size mismatch: sizeof Register.r11d != 8");
1703 .tag = tag,
1704 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
1705 .data = .{ .imm = 0x10 },
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 }
18871790 }
18881791}
src/arch/x86_64/Mir.zig-13
......@@ -136,19 +136,6 @@ pub const Inst = struct {
136136 cmp_scale_src,
137137 cmp_scale_dst,
138138 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`.
152139 mov,
153140 mov_scale_src,
154141 mov_scale_dst,