| ... | ... | @@ -93,46 +93,41 @@ pub fn emitMir(emit: *Emit) InnerError!void { |
| 93 | 93 | const inst = @intCast(u32, index); |
| 94 | 94 | try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); |
| 95 | 95 | 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 | |
| 136 | 131 | .movabs => try emit.mirMovabs(inst), |
| 137 | 132 | |
| 138 | 133 | .lea => try emit.mirLea(inst), |
| ... | ... | @@ -140,19 +135,19 @@ pub fn emitMir(emit: *Emit) InnerError!void { |
| 140 | 135 | |
| 141 | 136 | .imul_complex => try emit.mirIMulComplex(inst), |
| 142 | 137 | |
| 143 | | .push => try emit.mirPushPop(.push, inst), |
| 144 | | .pop => try emit.mirPushPop(.pop, inst), |
| 138 | .push, .pop => try emit.mirPushPop(tag, inst), |
| 145 | 139 | |
| 146 | | .jmp => try emit.mirJmpCall(.jmp, inst), |
| 147 | | .call => try emit.mirJmpCall(.call, inst), |
| 140 | .jmp, .call => try emit.mirJmpCall(tag, inst), |
| 148 | 141 | |
| 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), |
| 152 | 146 | |
| 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), |
| 156 | 151 | |
| 157 | 152 | .ret => try emit.mirRet(inst), |
| 158 | 153 | |
| ... | ... | @@ -598,6 +593,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode { |
| 598 | 593 | .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 }, |
| 599 | 594 | .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 }, |
| 600 | 595 | .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 }, |
| 596 | .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 }, |
| 601 | 597 | else => unreachable, |
| 602 | 598 | }, |
| 603 | 599 | .mr => { |
| ... | ... | @@ -610,6 +606,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode { |
| 610 | 606 | .@"or" => 0x09, |
| 611 | 607 | .sbb => 0x19, |
| 612 | 608 | .cmp => 0x39, |
| 609 | .mov => 0x89, |
| 613 | 610 | else => unreachable, |
| 614 | 611 | }; |
| 615 | 612 | return .{ .opc = opc, .modrm_ext = undefined }; |
| ... | ... | @@ -624,6 +621,7 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode { |
| 624 | 621 | .@"or" => 0x0b, |
| 625 | 622 | .sbb => 0x1b, |
| 626 | 623 | .cmp => 0x3b, |
| 624 | .mov => 0x8b, |
| 627 | 625 | else => unreachable, |
| 628 | 626 | }; |
| 629 | 627 | return .{ .opc = opc, .modrm_ext = undefined }; |
| ... | ... | @@ -632,262 +630,9 @@ inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode { |
| 632 | 630 | } |
| 633 | 631 | |
| 634 | 632 | fn 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 | | |
| 790 | | fn 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 | | |
| 815 | | fn 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 | | |
| 863 | | fn 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 | | |
| 888 | | fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 889 | | const res = try mirMovImpl( |
| 633 | const res = try mirArithImpl( |
| 890 | 634 | emit.bin_file.allocator, |
| 635 | tag, |
| 891 | 636 | emit.mir.instructions, |
| 892 | 637 | emit.mir.extra, |
| 893 | 638 | inst, |
| ... | ... | @@ -900,8 +645,9 @@ fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 900 | 645 | } |
| 901 | 646 | } |
| 902 | 647 | |
| 903 | | fn mirMovImpl( |
| 648 | fn mirArithImpl( |
| 904 | 649 | allocator: Allocator, |
| 650 | tag: Mir.Inst.Tag, |
| 905 | 651 | mir_instructions: std.MultiArrayList(Mir.Inst).Slice, |
| 906 | 652 | mir_extra: []const u32, |
| 907 | 653 | inst: Mir.Inst.Index, |
| ... | ... | @@ -915,8 +661,8 @@ fn mirMovImpl( |
| 915 | 661 | // mov reg1, imm32 |
| 916 | 662 | // MI |
| 917 | 663 | 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; |
| 920 | 666 | const encoder = try Encoder.init(code, 7); |
| 921 | 667 | if (ops.reg1.size() == 16) { |
| 922 | 668 | // 0x66 prefix switches to the non-default size; here we assume a switch from |
| ... | ... | @@ -929,7 +675,7 @@ fn mirMovImpl( |
| 929 | 675 | .b = ops.reg1.isExtended(), |
| 930 | 676 | }); |
| 931 | 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 | 679 | switch (ops.reg1.size()) { |
| 934 | 680 | 8 => { |
| 935 | 681 | const imm8 = math.cast(i8, imm) catch { |
| ... | ... | @@ -974,7 +720,8 @@ fn mirMovImpl( |
| 974 | 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 | 725 | const encoder = try Encoder.init(code, 3); |
| 979 | 726 | encoder.rex(.{ |
| 980 | 727 | .w = ops.reg1.size() == 64 and ops.reg2.size() == 64, |
| ... | ... | @@ -986,7 +733,8 @@ fn mirMovImpl( |
| 986 | 733 | }, |
| 987 | 734 | 0b01 => blk: { |
| 988 | 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 | 738 | if (ops.reg2 == .none) { |
| 991 | 739 | // mov reg1, [imm32] |
| 992 | 740 | // RM |
| ... | ... | @@ -1045,15 +793,14 @@ fn mirMovImpl( |
| 1045 | 793 | // a byte, word or dword ptr. |
| 1046 | 794 | // TODO we currently don't have a way to flag imm32 64bit sign extended |
| 1047 | 795 | 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); |
| 1050 | 797 | const encoder = try Encoder.init(code, 7); |
| 1051 | 798 | encoder.rex(.{ |
| 1052 | 799 | .w = false, |
| 1053 | 800 | .b = ops.reg1.isExtended(), |
| 1054 | 801 | }); |
| 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()); |
| 1057 | 804 | encoder.imm32(imm); |
| 1058 | 805 | break :blk; |
| 1059 | 806 | } |
| ... | ... | @@ -1066,7 +813,8 @@ fn mirMovImpl( |
| 1066 | 813 | // * reg2 is 16bit - word ptr |
| 1067 | 814 | // * reg2 is 8bit - byte ptr |
| 1068 | 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 | 818 | const encoder = try Encoder.init(code, 5); |
| 1071 | 819 | if (ops.reg2.size() == 16) { |
| 1072 | 820 | encoder.opcode_1byte(0x66); |
| ... | ... | @@ -1101,19 +849,18 @@ fn mirMovImpl( |
| 1101 | 849 | const payload = mir_instructions.items(.data)[inst].payload; |
| 1102 | 850 | const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data; |
| 1103 | 851 | const imm_op = imm_pair.operand; |
| 1104 | | const opc: u8 = 0xc7; |
| 1105 | | const modrm_ext: u3 = 0x0; |
| 852 | const opcode = getArithOpCode(tag, .mi); |
| 1106 | 853 | const encoder = try Encoder.init(code, 10); |
| 1107 | 854 | encoder.rex(.{ |
| 1108 | 855 | .w = false, |
| 1109 | 856 | .b = ops.reg1.isExtended(), |
| 1110 | 857 | }); |
| 1111 | | encoder.opcode_1byte(opc); |
| 858 | encoder.opcode_1byte(opcode.opc); |
| 1112 | 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 | 861 | encoder.disp8(@intCast(i8, imm_pair.dest_off)); |
| 1115 | 862 | } else { |
| 1116 | | encoder.modRm_indirectDisp32(modrm_ext, ops.reg1.lowId()); |
| 863 | encoder.modRm_indirectDisp32(opcode.modrm_ext, ops.reg1.lowId()); |
| 1117 | 864 | encoder.disp32(imm_pair.dest_off); |
| 1118 | 865 | } |
| 1119 | 866 | encoder.imm32(imm_op); |
| ... | ... | @@ -1137,6 +884,104 @@ fn immOpSize(imm: i32) u8 { |
| 1137 | 884 | return 32; |
| 1138 | 885 | } |
| 1139 | 886 | |
| 887 | fn 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 | |
| 912 | fn 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 | |
| 960 | fn 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 | |
| 1140 | 985 | fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1141 | 986 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 1142 | 987 | assert(tag == .movabs); |
| ... | ... | @@ -1552,8 +1397,9 @@ const Mock = struct { |
| 1552 | 1397 | const code_index = self.code.items.len; |
| 1553 | 1398 | const mir_index = try self.addInst(mir_inst); |
| 1554 | 1399 | const res = switch (mir_inst.tag) { |
| 1555 | | .mov => try mirMovImpl( |
| 1400 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl( |
| 1556 | 1401 | testing.allocator, |
| 1402 | mir_inst.tag, |
| 1557 | 1403 | self.mir_instructions.slice(), |
| 1558 | 1404 | self.mir_extra.items, |
| 1559 | 1405 | mir_index, |
| ... | ... | @@ -1575,8 +1421,9 @@ const Mock = struct { |
| 1575 | 1421 | const dummy_src_loc = Mock.dummySrcLoc(); |
| 1576 | 1422 | const index = try self.addInst(mir_inst); |
| 1577 | 1423 | const res = switch (mir_inst.tag) { |
| 1578 | | .mov => try mirMovImpl( |
| 1424 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try mirArithImpl( |
| 1579 | 1425 | testing.allocator, |
| 1426 | mir_inst.tag, |
| 1580 | 1427 | self.mir_instructions.slice(), |
| 1581 | 1428 | self.mir_extra.items, |
| 1582 | 1429 | index, |
| ... | ... | @@ -1611,278 +1458,334 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []co |
| 1611 | 1458 | return error.TestFailed; |
| 1612 | 1459 | } |
| 1613 | 1460 | |
| 1614 | | test "mov dst_reg, src_reg" { |
| 1461 | test "ARITH_OP/MOV dst_reg, src_reg" { |
| 1615 | 1462 | var mock = Mock.init(); |
| 1616 | 1463 | 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 | } |
| 1649 | 1500 | } |
| 1650 | 1501 | |
| 1651 | | test "mov dst_reg, imm" { |
| 1502 | test "ARITH_OP/MOV dst_reg, imm" { |
| 1652 | 1503 | var mock = Mock.init(); |
| 1653 | 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 | | } |
| 1693 | 1505 | |
| 1694 | | test "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 | } |
| 1722 | 1556 | } |
| 1723 | 1557 | |
| 1724 | | test "mov dst_reg, [src_reg + imm]" { |
| 1558 | test "ARITH_OP/MOV dst_reg, [imm32]" { |
| 1725 | 1559 | var mock = Mock.init(); |
| 1726 | 1560 | 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 | } |
| 1747 | 1591 | } |
| 1748 | 1592 | |
| 1749 | | test "mov [dst_reg + 0], imm" { |
| 1593 | test "ARITH_OP/MOV dst_reg, [src_reg + imm]" { |
| 1750 | 1594 | var mock = Mock.init(); |
| 1751 | 1595 | 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 | } |
| 1777 | 1621 | } |
| 1778 | 1622 | |
| 1779 | | test "mov [dst_reg + imm32], src_reg" { |
| 1623 | test "ARITH_OP/MOV [dst_reg + 0], imm" { |
| 1780 | 1624 | var mock = Mock.init(); |
| 1781 | 1625 | 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 | } |
| 1817 | 1663 | } |
| 1818 | 1664 | |
| 1819 | | test "mov [dst_reg + imm32], imm32" { |
| 1665 | test "ARITH_OP/MOV [dst_reg + imm32], src_reg" { |
| 1820 | 1666 | var mock = Mock.init(); |
| 1821 | 1667 | 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}; |
| 1827 | 1672 | 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"); |
| 1838 | 1677 | 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"); |
| 1849 | 1682 | 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"); |
| 1860 | 1687 | 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"); |
| 1871 | 1692 | 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"); |
| 1882 | 1702 | 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 | |
| 1710 | test "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 | } |