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