authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-12 08:41:44+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-12 08:41:44+01:00
log707a74655be9fd702cb1be84baa719b29435fcf7
tree5a01bc7f9c6e93e8b505ecadd463e316c7045e59
parent433558a92f005d3ad68528c62ffd6006c48b80bd

x86_64: downstream encoder/assembler tests


1 files changed, 1489 insertions(+), 0 deletions(-)

src/arch/x86_64/encoder.zig+1489
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const log = std.log.scoped(.x86_64_encoder);3const log = std.log.scoped(.x86_64_encoder);
4const math = std.math;4const math = std.math;
5const testing = std.testing;
56
6const bits = @import("bits.zig");7const bits = @import("bits.zig");
7const Encoding = @import("Encoding.zig");8const Encoding = @import("Encoding.zig");
...@@ -784,3 +785,1491 @@ pub const Rex = struct {...@@ -784,3 +785,1491 @@ pub const Rex = struct {
784 return rex.w or rex.r or rex.x or rex.b;785 return rex.w or rex.r or rex.x or rex.b;
785 }786 }
786};787};
788
789// Tests
790fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
791 assert(expected.len > 0);
792 if (std.mem.eql(u8, expected, given)) return;
793 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)});
794 defer testing.allocator.free(expected_fmt);
795 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)});
796 defer testing.allocator.free(given_fmt);
797 const idx = std.mem.indexOfDiff(u8, expected_fmt, given_fmt).?;
798 var padding = try testing.allocator.alloc(u8, idx + 5);
799 defer testing.allocator.free(padding);
800 std.mem.set(u8, padding, ' ');
801 std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{
802 assembly,
803 expected_fmt,
804 given_fmt,
805 padding,
806 });
807 return error.TestFailed;
808}
809
810const TestEncode = struct {
811 buffer: [32]u8 = undefined,
812 index: usize = 0,
813
814 fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: struct {
815 op1: Instruction.Operand = .none,
816 op2: Instruction.Operand = .none,
817 op3: Instruction.Operand = .none,
818 op4: Instruction.Operand = .none,
819 }) !void {
820 var stream = std.io.fixedBufferStream(&enc.buffer);
821 var count_writer = std.io.countingWriter(stream.writer());
822 const inst = try Instruction.new(mnemonic, .{
823 .op1 = args.op1,
824 .op2 = args.op2,
825 .op3 = args.op3,
826 .op4 = args.op4,
827 });
828 try inst.encode(count_writer.writer());
829 enc.index = count_writer.bytes_written;
830 }
831
832 fn code(enc: TestEncode) []const u8 {
833 return enc.buffer[0..enc.index];
834 }
835};
836
837test "encode" {
838 var buf = std.ArrayList(u8).init(testing.allocator);
839 defer buf.deinit();
840
841 const inst = try Instruction.new(.mov, .{
842 .op1 = .{ .reg = .rbx },
843 .op2 = .{ .imm = Immediate.u(4) },
844 });
845 try inst.encode(buf.writer());
846 try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
847}
848
849test "lower I encoding" {
850 var enc = TestEncode{};
851
852 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10) } });
853 try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
854
855 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000) } });
856 try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
857
858 try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10000000) } });
859 try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
860
861 try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10000000) } });
862 try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
863
864 try enc.encode(.add, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } });
865 try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
866
867 try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
868 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
869
870 try enc.encode(.sbb, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .imm = Immediate.u(0x10) } });
871 try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
872
873 try enc.encode(.xor, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } });
874 try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
875}
876
877test "lower MI encoding" {
878 var enc = TestEncode{};
879
880 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
881 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
882
883 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
884 .base = .r12,
885 .disp = 0,
886 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
887 try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
888
889 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
890 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
891
892 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } });
893 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
894
895 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
896 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
897
898 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
899 .base = .r11,
900 .disp = 0,
901 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
902 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
903
904 try enc.encode(.mov, .{
905 .op1 = .{ .mem = Memory.rip(.qword, 0x10) },
906 .op2 = .{ .imm = Immediate.u(0x10) },
907 });
908 try expectEqualHexStrings(
909 "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
910 enc.code(),
911 "mov QWORD PTR [rip + 0x10], 0x10",
912 );
913
914 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
915 .base = .rbp,
916 .disp = -8,
917 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
918 try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
919
920 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{
921 .base = .rbp,
922 .disp = -2,
923 }) }, .op2 = .{ .imm = Immediate.s(-16) } });
924 try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
925
926 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
927 .base = .rbp,
928 .disp = -1,
929 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
930 try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
931
932 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
933 .base = .ds,
934 .disp = 0x10000000,
935 .scale_index = .{
936 .scale = 2,
937 .index = .rcx,
938 },
939 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
940 try expectEqualHexStrings(
941 "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
942 enc.code(),
943 "mov QWORD PTR [rcx*2 + 0x10000000], 0x10",
944 );
945
946 try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
947 .base = .rbp,
948 .disp = -0x10,
949 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
950 try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
951
952 try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) }, .op2 = .{ .imm = Immediate.u(0x10) } });
953 try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
954
955 try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
956 try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
957
958 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
959 .base = .rdx,
960 .disp = -8,
961 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
962 try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
963
964 try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } });
965 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
966
967 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
968 .base = .rbp,
969 .disp = -0x10,
970 }) }, .op2 = .{ .imm = Immediate.s(-0x10) } });
971 try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
972
973 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
974 .base = .ds,
975 .disp = 0x10000000,
976 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
977 try expectEqualHexStrings(
978 "\x83\x24\x25\x00\x00\x00\x10\x10",
979 enc.code(),
980 "and DWORD PTR ds:0x10000000, 0x10",
981 );
982
983 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
984 .base = .es,
985 .disp = 0x10000000,
986 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
987 try expectEqualHexStrings(
988 "\x26\x83\x24\x25\x00\x00\x00\x10\x10",
989 enc.code(),
990 "and DWORD PTR es:0x10000000, 0x10",
991 );
992
993 try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{
994 .base = .r12,
995 .disp = 0x10000000,
996 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
997 try expectEqualHexStrings(
998 "\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
999 enc.code(),
1000 "and DWORD PTR [r12 + 0x10000000], 0x10",
1001 );
1002
1003 try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1004 .base = .r11,
1005 .disp = 0x10000000,
1006 }) }, .op2 = .{ .imm = Immediate.u(0x10) } });
1007 try expectEqualHexStrings(
1008 "\x41\x83\xAB\x00\x00\x00\x10\x10",
1009 enc.code(),
1010 "sub DWORD PTR [r11 + 0x10000000], 0x10",
1011 );
1012}
1013
1014test "lower RM encoding" {
1015 var enc = TestEncode{};
1016
1017 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1018 .base = .r11,
1019 .disp = 0,
1020 }) } });
1021 try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
1022
1023 try enc.encode(.mov, .{ .op1 = .{ .reg = .rbx }, .op2 = .{ .mem = Memory.sib(.qword, .{
1024 .base = .ds,
1025 .disp = 0x10,
1026 }) } });
1027 try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
1028
1029 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1030 .base = .rbp,
1031 .disp = -4,
1032 }) } });
1033 try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
1034
1035 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1036 .base = .rbp,
1037 .scale_index = .{
1038 .scale = 1,
1039 .index = .rcx,
1040 },
1041 .disp = -8,
1042 }) } });
1043 try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]");
1044
1045 try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.dword, .{
1046 .base = .rbp,
1047 .scale_index = .{
1048 .scale = 4,
1049 .index = .rdx,
1050 },
1051 .disp = -4,
1052 }) } });
1053 try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
1054
1055 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{
1056 .base = .rbp,
1057 .scale_index = .{
1058 .scale = 8,
1059 .index = .rcx,
1060 },
1061 .disp = -8,
1062 }) } });
1063 try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]");
1064
1065 try enc.encode(.mov, .{ .op1 = .{ .reg = .r8b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1066 .base = .rsi,
1067 .scale_index = .{
1068 .scale = 1,
1069 .index = .rcx,
1070 },
1071 .disp = -24,
1072 }) } });
1073 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]");
1074
1075 // TODO this mnemonic needs cleanup as some prefixes are obsolete.
1076 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .cs } });
1077 try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
1078
1079 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1080 .base = .rbp,
1081 .disp = -16,
1082 }) }, .op2 = .{ .reg = .fs } });
1083 try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
1084
1085 try enc.encode(.mov, .{ .op1 = .{ .reg = .r12w }, .op2 = .{ .reg = .cs } });
1086 try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
1087
1088 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{
1089 .base = .rbp,
1090 .disp = -16,
1091 }) }, .op2 = .{ .reg = .fs } });
1092 try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
1093
1094 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bx } });
1095 try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx");
1096
1097 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bl } });
1098 try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl");
1099
1100 try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .reg = .bl } });
1101 try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl");
1102
1103 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.word, .{
1104 .base = .rbp,
1105 .disp = 0,
1106 }) } });
1107 try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
1108
1109 try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.byte, .{
1110 .base = null,
1111 .scale_index = .{
1112 .index = .rax,
1113 .scale = 2,
1114 },
1115 .disp = 0,
1116 }) } });
1117 try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]");
1118
1119 try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } });
1120 try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]");
1121
1122 try enc.encode(.movsx, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .bx } });
1123 try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx");
1124
1125 try enc.encode(.movsxd, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .ebx } });
1126 try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx");
1127
1128 try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.qword, 0x10) } });
1129 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]");
1130
1131 try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } });
1132 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]");
1133
1134 try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } });
1135 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
1136
1137 try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.word, 0x10) } });
1138 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
1139
1140 try enc.encode(.lea, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } });
1141 try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]");
1142
1143 try enc.encode(.lea, .{ .op1 = .{ .reg = .rsi }, .op2 = .{ .mem = Memory.sib(.qword, .{
1144 .base = .rbp,
1145 .scale_index = .{
1146 .scale = 1,
1147 .index = .rcx,
1148 },
1149 .disp = 0,
1150 }) } });
1151 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]");
1152
1153 try enc.encode(.add, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1154 .base = .ds,
1155 .disp = 0x10000000,
1156 }) } });
1157 try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
1158
1159 try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1160 .base = .ds,
1161 .disp = 0x10000000,
1162 }) } });
1163 try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
1164
1165 try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{
1166 .base = .fs,
1167 .disp = 0x10000000,
1168 }) } });
1169 try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
1170
1171 try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1172 .base = .r13,
1173 .disp = 0x10000000,
1174 }) } });
1175 try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
1176
1177 try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{
1178 .base = .r12,
1179 .disp = 0x10000000,
1180 }) } });
1181 try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
1182
1183 try enc.encode(.imul, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .reg = .r12 } });
1184 try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12");
1185}
1186
1187test "lower RMI encoding" {
1188 var enc = TestEncode{};
1189
1190 try enc.encode(.imul, .{
1191 .op1 = .{ .reg = .r11 },
1192 .op2 = .{ .reg = .r12 },
1193 .op3 = .{ .imm = Immediate.s(-2) },
1194 });
1195 try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
1196
1197 try enc.encode(.imul, .{
1198 .op1 = .{ .reg = .r11 },
1199 .op2 = .{ .mem = Memory.rip(.qword, -16) },
1200 .op3 = .{ .imm = Immediate.s(-1024) },
1201 });
1202 try expectEqualHexStrings(
1203 "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
1204 enc.code(),
1205 "imul r11, QWORD PTR [rip - 16], -1024",
1206 );
1207
1208 try enc.encode(.imul, .{
1209 .op1 = .{ .reg = .bx },
1210 .op2 = .{ .mem = Memory.sib(.word, .{
1211 .base = .rbp,
1212 .disp = -16,
1213 }) },
1214 .op3 = .{ .imm = Immediate.s(-1024) },
1215 });
1216 try expectEqualHexStrings(
1217 "\x66\x69\x5D\xF0\x00\xFC",
1218 enc.code(),
1219 "imul bx, WORD PTR [rbp - 16], -1024",
1220 );
1221
1222 try enc.encode(.imul, .{
1223 .op1 = .{ .reg = .bx },
1224 .op2 = .{ .mem = Memory.sib(.word, .{
1225 .base = .rbp,
1226 .disp = -16,
1227 }) },
1228 .op3 = .{ .imm = Immediate.u(1024) },
1229 });
1230 try expectEqualHexStrings(
1231 "\x66\x69\x5D\xF0\x00\x04",
1232 enc.code(),
1233 "imul bx, WORD PTR [rbp - 16], 1024",
1234 );
1235}
1236
1237test "lower MR encoding" {
1238 var enc = TestEncode{};
1239
1240 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } });
1241 try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
1242
1243 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1244 .base = .rbp,
1245 .disp = -4,
1246 }) }, .op2 = .{ .reg = .r11 } });
1247 try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
1248
1249 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, .op2 = .{ .reg = .r12 } });
1250 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12");
1251
1252 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1253 .base = .r11,
1254 .scale_index = .{
1255 .scale = 2,
1256 .index = .r12,
1257 },
1258 .disp = 0x10,
1259 }) }, .op2 = .{ .reg = .r13 } });
1260 try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
1261
1262 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, -0x10) }, .op2 = .{ .reg = .r12w } });
1263 try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w");
1264
1265 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
1266 .base = .r11,
1267 .scale_index = .{
1268 .scale = 2,
1269 .index = .r12,
1270 },
1271 .disp = 0x10,
1272 }) }, .op2 = .{ .reg = .r13b } });
1273 try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
1274
1275 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.byte, .{
1276 .base = .ds,
1277 .disp = 0x10000000,
1278 }) }, .op2 = .{ .reg = .r12b } });
1279 try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
1280
1281 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1282 .base = .ds,
1283 .disp = 0x10000000,
1284 }) }, .op2 = .{ .reg = .r12d } });
1285 try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
1286
1287 try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{
1288 .base = .gs,
1289 .disp = 0x10000000,
1290 }) }, .op2 = .{ .reg = .r12d } });
1291 try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
1292
1293 try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1294 .base = .r11,
1295 .disp = 0x10000000,
1296 }) }, .op2 = .{ .reg = .r12 } });
1297 try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
1298}
1299
1300test "lower M encoding" {
1301 var enc = TestEncode{};
1302
1303 try enc.encode(.call, .{ .op1 = .{ .reg = .r12 } });
1304 try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
1305
1306 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1307 .base = .r12,
1308 .disp = 0,
1309 }) } });
1310 try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
1311
1312 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1313 .base = null,
1314 .scale_index = .{
1315 .index = .r11,
1316 .scale = 2,
1317 },
1318 .disp = 0,
1319 }) } });
1320 try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
1321
1322 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1323 .base = null,
1324 .scale_index = .{
1325 .index = .r12,
1326 .scale = 2,
1327 },
1328 .disp = 0,
1329 }) } });
1330 try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
1331
1332 try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1333 .base = .gs,
1334 .disp = 0,
1335 }) } });
1336 try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
1337
1338 try enc.encode(.call, .{ .op1 = .{ .imm = Immediate.s(0) } });
1339 try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
1340
1341 try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.qword, .{
1342 .base = .rbp,
1343 .disp = 0,
1344 }) } });
1345 try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1346
1347 try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.word, .{
1348 .base = .rbp,
1349 .disp = 0,
1350 }) } });
1351 try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1352
1353 try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) } });
1354 try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
1355
1356 try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } });
1357 try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]");
1358
1359 try enc.encode(.imul, .{ .op1 = .{ .reg = .rax } });
1360 try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax");
1361
1362 try enc.encode(.imul, .{ .op1 = .{ .reg = .r12 } });
1363 try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12");
1364}
1365
1366test "lower O encoding" {
1367 var enc = TestEncode{};
1368
1369 try enc.encode(.push, .{ .op1 = .{ .reg = .rax } });
1370 try expectEqualHexStrings("\x50", enc.code(), "push rax");
1371
1372 try enc.encode(.push, .{ .op1 = .{ .reg = .r12w } });
1373 try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w");
1374
1375 try enc.encode(.pop, .{ .op1 = .{ .reg = .r12 } });
1376 try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12");
1377}
1378
1379test "lower OI encoding" {
1380 var enc = TestEncode{};
1381
1382 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } });
1383 try expectEqualHexStrings(
1384 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
1385 enc.code(),
1386 "movabs rax, 0x1000000000000000",
1387 );
1388
1389 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } });
1390 try expectEqualHexStrings(
1391 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
1392 enc.code(),
1393 "movabs r11, 0x1000000000000000",
1394 );
1395
1396 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11d }, .op2 = .{ .imm = Immediate.u(0x10000000) } });
1397 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
1398
1399 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11w }, .op2 = .{ .imm = Immediate.u(0x1000) } });
1400 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
1401
1402 try enc.encode(.mov, .{ .op1 = .{ .reg = .r11b }, .op2 = .{ .imm = Immediate.u(0x10) } });
1403 try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
1404}
1405
1406test "lower FD/TD encoding" {
1407 var enc = TestEncode{};
1408
1409 try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.moffs(.cs, 0x10) } });
1410 try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10");
1411
1412 try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.moffs(.fs, 0x10) } });
1413 try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10");
1414
1415 try enc.encode(.mov, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.moffs(.gs, 0x10) } });
1416 try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10");
1417
1418 try enc.encode(.mov, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.moffs(.ds, 0x10) } });
1419 try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10");
1420
1421 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.cs, 0x10) }, .op2 = .{ .reg = .rax } });
1422 try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax");
1423
1424 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.fs, 0x10) }, .op2 = .{ .reg = .eax } });
1425 try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax");
1426
1427 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.gs, 0x10) }, .op2 = .{ .reg = .ax } });
1428 try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax");
1429
1430 try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.ds, 0x10) }, .op2 = .{ .reg = .al } });
1431 try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al");
1432}
1433
1434test "lower NP encoding" {
1435 var enc = TestEncode{};
1436
1437 try enc.encode(.int3, .{});
1438 try expectEqualHexStrings("\xCC", enc.code(), "int3");
1439
1440 try enc.encode(.nop, .{});
1441 try expectEqualHexStrings("\x90", enc.code(), "nop");
1442
1443 try enc.encode(.ret, .{});
1444 try expectEqualHexStrings("\xC3", enc.code(), "ret");
1445
1446 try enc.encode(.syscall, .{});
1447 try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall");
1448}
1449
1450fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: struct {
1451 op1: Instruction.Operand = .none,
1452 op2: Instruction.Operand = .none,
1453 op3: Instruction.Operand = .none,
1454 op4: Instruction.Operand = .none,
1455}) !void {
1456 const err = Instruction.new(mnemonic, .{
1457 .op1 = args.op1,
1458 .op2 = args.op2,
1459 .op3 = args.op3,
1460 .op4 = args.op4,
1461 });
1462 try testing.expectError(error.InvalidInstruction, err);
1463}
1464
1465test "invalid instruction" {
1466 try invalidInstruction(.call, .{ .op1 = .{ .reg = .eax } });
1467 try invalidInstruction(.call, .{ .op1 = .{ .reg = .ax } });
1468 try invalidInstruction(.call, .{ .op1 = .{ .reg = .al } });
1469 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.dword, 0) } });
1470 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } });
1471 try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.byte, 0) } });
1472 try invalidInstruction(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, 0x10) }, .op2 = .{ .reg = .r12 } });
1473 try invalidInstruction(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } });
1474 try invalidInstruction(.lea, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.rip(.byte, 0) } });
1475 try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12b } });
1476 try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12d } });
1477 try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12b } });
1478 try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12d } });
1479 try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } });
1480}
1481
1482fn cannotEncode(mnemonic: Instruction.Mnemonic, args: struct {
1483 op1: Instruction.Operand = .none,
1484 op2: Instruction.Operand = .none,
1485 op3: Instruction.Operand = .none,
1486 op4: Instruction.Operand = .none,
1487}) !void {
1488 try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, .{
1489 .op1 = args.op1,
1490 .op2 = args.op2,
1491 .op3 = args.op3,
1492 .op4 = args.op4,
1493 }));
1494}
1495
1496test "cannot encode" {
1497 try cannotEncode(.@"test", .{
1498 .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12, .disp = 0 }) },
1499 .op2 = .{ .reg = .ah },
1500 });
1501 try cannotEncode(.@"test", .{
1502 .op1 = .{ .reg = .r11b },
1503 .op2 = .{ .reg = .bh },
1504 });
1505 try cannotEncode(.mov, .{
1506 .op1 = .{ .reg = .sil },
1507 .op2 = .{ .reg = .ah },
1508 });
1509}
1510
1511const Assembler = struct {
1512 it: Tokenizer,
1513
1514 const Tokenizer = struct {
1515 input: []const u8,
1516 pos: usize = 0,
1517
1518 const Error = error{InvalidToken};
1519
1520 const Token = struct {
1521 id: Id,
1522 start: usize,
1523 end: usize,
1524
1525 const Id = enum {
1526 eof,
1527
1528 space,
1529 new_line,
1530
1531 colon,
1532 comma,
1533 open_br,
1534 close_br,
1535 plus,
1536 minus,
1537 star,
1538
1539 string,
1540 numeral,
1541 };
1542 };
1543
1544 const Iterator = struct {};
1545
1546 fn next(it: *Tokenizer) !Token {
1547 var result = Token{
1548 .id = .eof,
1549 .start = it.pos,
1550 .end = it.pos,
1551 };
1552
1553 var state: enum {
1554 start,
1555 space,
1556 new_line,
1557 string,
1558 numeral,
1559 numeral_hex,
1560 } = .start;
1561
1562 while (it.pos < it.input.len) : (it.pos += 1) {
1563 const ch = it.input[it.pos];
1564 switch (state) {
1565 .start => switch (ch) {
1566 ',' => {
1567 result.id = .comma;
1568 it.pos += 1;
1569 break;
1570 },
1571 ':' => {
1572 result.id = .colon;
1573 it.pos += 1;
1574 break;
1575 },
1576 '[' => {
1577 result.id = .open_br;
1578 it.pos += 1;
1579 break;
1580 },
1581 ']' => {
1582 result.id = .close_br;
1583 it.pos += 1;
1584 break;
1585 },
1586 '+' => {
1587 result.id = .plus;
1588 it.pos += 1;
1589 break;
1590 },
1591 '-' => {
1592 result.id = .minus;
1593 it.pos += 1;
1594 break;
1595 },
1596 '*' => {
1597 result.id = .star;
1598 it.pos += 1;
1599 break;
1600 },
1601 ' ', '\t' => state = .space,
1602 '\n', '\r' => state = .new_line,
1603 'a'...'z', 'A'...'Z' => state = .string,
1604 '0'...'9' => state = .numeral,
1605 else => return error.InvalidToken,
1606 },
1607
1608 .space => switch (ch) {
1609 ' ', '\t' => {},
1610 else => {
1611 result.id = .space;
1612 break;
1613 },
1614 },
1615
1616 .new_line => switch (ch) {
1617 '\n', '\r', ' ', '\t' => {},
1618 else => {
1619 result.id = .new_line;
1620 break;
1621 },
1622 },
1623
1624 .string => switch (ch) {
1625 'a'...'z', 'A'...'Z', '0'...'9' => {},
1626 else => {
1627 result.id = .string;
1628 break;
1629 },
1630 },
1631
1632 .numeral => switch (ch) {
1633 'x' => state = .numeral_hex,
1634 '0'...'9' => {},
1635 else => {
1636 result.id = .numeral;
1637 break;
1638 },
1639 },
1640
1641 .numeral_hex => switch (ch) {
1642 'a'...'f' => {},
1643 '0'...'9' => {},
1644 else => {
1645 result.id = .numeral;
1646 break;
1647 },
1648 },
1649 }
1650 }
1651
1652 if (it.pos >= it.input.len) {
1653 switch (state) {
1654 .string => result.id = .string,
1655 .numeral, .numeral_hex => result.id = .numeral,
1656 else => {},
1657 }
1658 }
1659
1660 result.end = it.pos;
1661 return result;
1662 }
1663
1664 fn seekTo(it: *Tokenizer, pos: usize) void {
1665 it.pos = pos;
1666 }
1667 };
1668
1669 pub fn init(input: []const u8) Assembler {
1670 return .{
1671 .it = Tokenizer{ .input = input },
1672 };
1673 }
1674
1675 pub fn assemble(as: *Assembler, writer: anytype) !void {
1676 while (try as.next()) |parsed_inst| {
1677 const inst = try Instruction.new(parsed_inst.mnemonic, .{
1678 .op1 = parsed_inst.ops[0],
1679 .op2 = parsed_inst.ops[1],
1680 .op3 = parsed_inst.ops[2],
1681 .op4 = parsed_inst.ops[3],
1682 });
1683 try inst.encode(writer);
1684 }
1685 }
1686
1687 const ParseResult = struct {
1688 mnemonic: Instruction.Mnemonic,
1689 ops: [4]Instruction.Operand,
1690 };
1691
1692 const ParseError = error{
1693 UnexpectedToken,
1694 InvalidMnemonic,
1695 InvalidOperand,
1696 InvalidRegister,
1697 InvalidPtrSize,
1698 InvalidMemoryOperand,
1699 InvalidScaleIndex,
1700 } || Tokenizer.Error || std.fmt.ParseIntError;
1701
1702 fn next(as: *Assembler) ParseError!?ParseResult {
1703 try as.skip(2, .{ .space, .new_line });
1704 const mnemonic_tok = as.expect(.string) catch |err| switch (err) {
1705 error.UnexpectedToken => return if (try as.peek() == .eof) null else err,
1706 else => return err,
1707 };
1708 const mnemonic = mnemonicFromString(as.source(mnemonic_tok)) orelse
1709 return error.InvalidMnemonic;
1710 try as.skip(1, .{.space});
1711
1712 const rules = .{
1713 .{},
1714 .{.register},
1715 .{.memory},
1716 .{.immediate},
1717 .{ .register, .register },
1718 .{ .register, .memory },
1719 .{ .memory, .register },
1720 .{ .register, .immediate },
1721 .{ .memory, .immediate },
1722 .{ .register, .register, .immediate },
1723 .{ .register, .memory, .immediate },
1724 };
1725
1726 const pos = as.it.pos;
1727 inline for (rules) |rule| {
1728 var ops = [4]Instruction.Operand{ .none, .none, .none, .none };
1729 if (as.parseOperandRule(rule, &ops)) {
1730 return .{
1731 .mnemonic = mnemonic,
1732 .ops = ops,
1733 };
1734 } else |_| {
1735 as.it.seekTo(pos);
1736 }
1737 }
1738
1739 return error.InvalidOperand;
1740 }
1741
1742 fn source(as: *Assembler, token: Tokenizer.Token) []const u8 {
1743 return as.it.input[token.start..token.end];
1744 }
1745
1746 fn peek(as: *Assembler) Tokenizer.Error!Tokenizer.Token.Id {
1747 const pos = as.it.pos;
1748 const next_tok = try as.it.next();
1749 const id = next_tok.id;
1750 as.it.seekTo(pos);
1751 return id;
1752 }
1753
1754 fn expect(as: *Assembler, id: Tokenizer.Token.Id) ParseError!Tokenizer.Token {
1755 const next_tok_id = try as.peek();
1756 if (next_tok_id == id) return as.it.next();
1757 return error.UnexpectedToken;
1758 }
1759
1760 fn skip(as: *Assembler, comptime num: comptime_int, tok_ids: [num]Tokenizer.Token.Id) Tokenizer.Error!void {
1761 outer: while (true) {
1762 const pos = as.it.pos;
1763 const next_tok = try as.it.next();
1764 inline for (tok_ids) |tok_id| {
1765 if (next_tok.id == tok_id) continue :outer;
1766 }
1767 as.it.seekTo(pos);
1768 break;
1769 }
1770 }
1771
1772 fn mnemonicFromString(bytes: []const u8) ?Instruction.Mnemonic {
1773 const ti = @typeInfo(Instruction.Mnemonic).Enum;
1774 inline for (ti.fields) |field| {
1775 if (std.mem.eql(u8, bytes, field.name)) {
1776 return @field(Instruction.Mnemonic, field.name);
1777 }
1778 }
1779 return null;
1780 }
1781
1782 fn parseOperandRule(as: *Assembler, rule: anytype, ops: *[4]Instruction.Operand) ParseError!void {
1783 inline for (rule, 0..) |cond, i| {
1784 comptime assert(i < 4);
1785 if (i > 0) {
1786 _ = try as.expect(.comma);
1787 try as.skip(1, .{.space});
1788 }
1789 if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
1790 @compileError("invalid condition in the rule: " ++ @typeName(@TypeOf(cond)));
1791 }
1792 switch (cond) {
1793 .register => {
1794 const reg_tok = try as.expect(.string);
1795 const reg = registerFromString(as.source(reg_tok)) orelse
1796 return error.InvalidOperand;
1797 ops[i] = .{ .reg = reg };
1798 },
1799 .memory => {
1800 const mem = try as.parseMemory();
1801 ops[i] = .{ .mem = mem };
1802 },
1803 .immediate => {
1804 const is_neg = if (as.expect(.minus)) |_| true else |_| false;
1805 const imm_tok = try as.expect(.numeral);
1806 const imm: Immediate = if (is_neg) blk: {
1807 const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0);
1808 break :blk .{ .signed = imm * -1 };
1809 } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };
1810 ops[i] = .{ .imm = imm };
1811 },
1812 else => @compileError("unhandled enum literal " ++ @tagName(cond)),
1813 }
1814 try as.skip(1, .{.space});
1815 }
1816
1817 try as.skip(1, .{.space});
1818 const tok = try as.it.next();
1819 switch (tok.id) {
1820 .new_line, .eof => {},
1821 else => return error.InvalidOperand,
1822 }
1823 }
1824
1825 fn registerFromString(bytes: []const u8) ?Register {
1826 const ti = @typeInfo(Register).Enum;
1827 inline for (ti.fields) |field| {
1828 if (std.mem.eql(u8, bytes, field.name)) {
1829 return @field(Register, field.name);
1830 }
1831 }
1832 return null;
1833 }
1834
1835 fn parseMemory(as: *Assembler) ParseError!Memory {
1836 const ptr_size: ?Memory.PtrSize = blk: {
1837 const pos = as.it.pos;
1838 const ptr_size = as.parsePtrSize() catch |err| switch (err) {
1839 error.UnexpectedToken => {
1840 as.it.seekTo(pos);
1841 break :blk null;
1842 },
1843 else => return err,
1844 };
1845 break :blk ptr_size;
1846 };
1847
1848 try as.skip(1, .{.space});
1849
1850 // Supported rules and orderings.
1851 const rules = .{
1852 .{ .open_br, .base, .close_br }, // [ base ]
1853 .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ]
1854 .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ]
1855 .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ]
1856 .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ]
1857 .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ]
1858 .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ]
1859 .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ]
1860 .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ]
1861 .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ]
1862 .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ]
1863 .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ]
1864 .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ]
1865 .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ]
1866 .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ]
1867 .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ]
1868 .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ]
1869 .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
1870 .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
1871 .{ .base, .colon, .disp }, // seg:disp
1872 };
1873
1874 const pos = as.it.pos;
1875 inline for (rules) |rule| {
1876 if (as.parseMemoryRule(rule)) |res| {
1877 if (res.rip) {
1878 if (res.base != null or res.scale_index != null or res.offset != null)
1879 return error.InvalidMemoryOperand;
1880 return Memory.rip(ptr_size orelse .qword, res.disp orelse 0);
1881 }
1882 if (res.base) |base| {
1883 if (res.rip)
1884 return error.InvalidMemoryOperand;
1885 if (res.offset) |offset| {
1886 if (res.scale_index != null or res.disp != null)
1887 return error.InvalidMemoryOperand;
1888 return Memory.moffs(base, offset);
1889 }
1890 return Memory.sib(ptr_size orelse .qword, .{
1891 .base = base,
1892 .scale_index = res.scale_index,
1893 .disp = res.disp orelse 0,
1894 });
1895 }
1896 return error.InvalidMemoryOperand;
1897 } else |_| {
1898 as.it.seekTo(pos);
1899 }
1900 }
1901
1902 return error.InvalidOperand;
1903 }
1904
1905 const MemoryParseResult = struct {
1906 rip: bool = false,
1907 base: ?Register = null,
1908 scale_index: ?Memory.ScaleIndex = null,
1909 disp: ?i32 = null,
1910 offset: ?u64 = null,
1911 };
1912
1913 fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
1914 var res: MemoryParseResult = .{};
1915 inline for (rule, 0..) |cond, i| {
1916 if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
1917 @compileError("unsupported condition type in the rule: " ++ @typeName(@TypeOf(cond)));
1918 }
1919 switch (cond) {
1920 .open_br, .close_br, .plus, .minus, .star, .colon => {
1921 _ = try as.expect(cond);
1922 },
1923 .base => {
1924 const tok = try as.expect(.string);
1925 res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
1926 },
1927 .rip => {
1928 const tok = try as.expect(.string);
1929 if (!std.mem.eql(u8, as.source(tok), "rip")) return error.InvalidMemoryOperand;
1930 res.rip = true;
1931 },
1932 .index => {
1933 const tok = try as.expect(.string);
1934 const index = registerFromString(as.source(tok)) orelse
1935 return error.InvalidMemoryOperand;
1936 if (res.scale_index) |*si| {
1937 si.index = index;
1938 } else {
1939 res.scale_index = .{ .scale = 1, .index = index };
1940 }
1941 },
1942 .scale => {
1943 const tok = try as.expect(.numeral);
1944 const scale = try std.fmt.parseInt(u2, as.source(tok), 0);
1945 if (res.scale_index) |*si| {
1946 si.scale = scale;
1947 } else {
1948 res.scale_index = .{ .scale = scale, .index = undefined };
1949 }
1950 },
1951 .disp => {
1952 const tok = try as.expect(.numeral);
1953 const is_neg = blk: {
1954 if (i > 0) {
1955 if (rule[i - 1] == .minus) break :blk true;
1956 }
1957 break :blk false;
1958 };
1959 if (std.fmt.parseInt(i32, as.source(tok), 0)) |disp| {
1960 res.disp = if (is_neg) -1 * disp else disp;
1961 } else |err| switch (err) {
1962 error.Overflow => {
1963 if (is_neg) return err;
1964 if (res.base) |base| {
1965 if (base.class() != .segment) return err;
1966 }
1967 const offset = try std.fmt.parseInt(u64, as.source(tok), 0);
1968 res.offset = offset;
1969 },
1970 else => return err,
1971 }
1972 },
1973 else => @compileError("unhandled operand output type: " ++ @tagName(cond)),
1974 }
1975 try as.skip(1, .{.space});
1976 }
1977 return res;
1978 }
1979
1980 fn parsePtrSize(as: *Assembler) ParseError!Memory.PtrSize {
1981 const size = try as.expect(.string);
1982 try as.skip(1, .{.space});
1983 const ptr = try as.expect(.string);
1984
1985 const size_raw = as.source(size);
1986 const ptr_raw = as.source(ptr);
1987 const len = size_raw.len + ptr_raw.len + 1;
1988 var buf: ["qword ptr".len]u8 = undefined;
1989 if (len > buf.len) return error.InvalidPtrSize;
1990
1991 for (size_raw, 0..) |c, i| {
1992 buf[i] = std.ascii.toLower(c);
1993 }
1994 buf[size_raw.len] = ' ';
1995 for (ptr_raw, 0..) |c, i| {
1996 buf[size_raw.len + i + 1] = std.ascii.toLower(c);
1997 }
1998
1999 const slice = buf[0..len];
2000 if (std.mem.eql(u8, slice, "qword ptr")) return .qword;
2001 if (std.mem.eql(u8, slice, "dword ptr")) return .dword;
2002 if (std.mem.eql(u8, slice, "word ptr")) return .word;
2003 if (std.mem.eql(u8, slice, "byte ptr")) return .byte;
2004 if (std.mem.eql(u8, slice, "tbyte ptr")) return .tbyte;
2005 return error.InvalidPtrSize;
2006 }
2007};
2008
2009test "assemble" {
2010 const input =
2011 \\int3
2012 \\mov rax, rbx
2013 \\mov qword ptr [rbp], rax
2014 \\mov qword ptr [rbp - 16], rax
2015 \\mov qword ptr [16 + rbp], rax
2016 \\mov rax, 0x10
2017 \\mov byte ptr [rbp - 0x10], 0x10
2018 \\mov word ptr [rbp + r12], r11w
2019 \\mov word ptr [rbp + r12 * 2], r11w
2020 \\mov word ptr [rbp + r12 * 2 - 16], r11w
2021 \\mov dword ptr [rip - 16], r12d
2022 \\mov rax, fs:0x0
2023 \\mov rax, gs:0x1000000000000000
2024 \\movzx r12, al
2025 \\imul r12, qword ptr [rbp - 16], 6
2026 \\jmp 0x0
2027 \\jc 0x0
2028 \\jb 0x0
2029 \\sal rax, 1
2030 \\sal rax, 63
2031 \\shl rax, 63
2032 \\sar rax, 63
2033 \\shr rax, 63
2034 \\test byte ptr [rbp - 16], r12b
2035 \\sal r12, cl
2036 \\mul qword ptr [rip - 16]
2037 \\div r12
2038 \\idiv byte ptr [rbp - 16]
2039 \\cwde
2040 \\cbw
2041 \\cdqe
2042 \\test byte ptr [rbp], ah
2043 \\test byte ptr [r12], spl
2044 \\cdq
2045 \\cwd
2046 \\cqo
2047 \\test bl, 0x1
2048 \\mov rbx,0x8000000000000000
2049 \\movss xmm0, dword ptr [rbp]
2050 \\movss xmm0, xmm1
2051 \\movss dword ptr [rbp - 16 + rax * 2], xmm7
2052 \\movss dword ptr [rbp - 16 + rax * 2], xmm8
2053 \\movss xmm15, xmm9
2054 \\movsd xmm8, qword ptr [rbp - 16]
2055 \\movsd qword ptr [rbp - 8], xmm0
2056 \\movq xmm8, qword ptr [rbp - 16]
2057 \\movq qword ptr [rbp - 16], xmm8
2058 \\ucomisd xmm0, qword ptr [rbp - 16]
2059 \\fisttp qword ptr [rbp - 16]
2060 \\fisttp word ptr [rip + 32]
2061 \\fisttp dword ptr [rax]
2062 \\fld tbyte ptr [rbp]
2063 \\fld dword ptr [rbp]
2064 \\xor bl, 0xff
2065 \\ud2
2066 \\add rsp, -1
2067 \\add rsp, 0xff
2068 \\mov sil, byte ptr [rax + rcx * 1]
2069 \\
2070 ;
2071
2072 // zig fmt: off
2073 const expected = &[_]u8{
2074 0xCC,
2075 0x48, 0x89, 0xD8,
2076 0x48, 0x89, 0x45, 0x00,
2077 0x48, 0x89, 0x45, 0xF0,
2078 0x48, 0x89, 0x45, 0x10,
2079 0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00,
2080 0xC6, 0x45, 0xF0, 0x10,
2081 0x66, 0x46, 0x89, 0x5C, 0x25, 0x00,
2082 0x66, 0x46, 0x89, 0x5C, 0x65, 0x00,
2083 0x66, 0x46, 0x89, 0x5C, 0x65, 0xF0,
2084 0x44, 0x89, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2085 0x64, 0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00,
2086 0x65, 0x48, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
2087 0x4C, 0x0F, 0xB6, 0xE0,
2088 0x4C, 0x6B, 0x65, 0xF0, 0x06,
2089 0xE9, 0x00, 0x00, 0x00, 0x00,
2090 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2091 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2092 0x48, 0xD1, 0xE0,
2093 0x48, 0xC1, 0xE0, 0x3F,
2094 0x48, 0xC1, 0xE0, 0x3F,
2095 0x48, 0xC1, 0xF8, 0x3F,
2096 0x48, 0xC1, 0xE8, 0x3F,
2097 0x44, 0x84, 0x65, 0xF0,
2098 0x49, 0xD3, 0xE4,
2099 0x48, 0xF7, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2100 0x49, 0xF7, 0xF4,
2101 0xF6, 0x7D, 0xF0,
2102 0x98,
2103 0x66, 0x98,
2104 0x48, 0x98,
2105 0x84, 0x65, 0x00,
2106 0x41, 0x84, 0x24, 0x24,
2107 0x99,
2108 0x66, 0x99,
2109 0x48, 0x99,
2110 0xF6, 0xC3, 0x01,
2111 0x48, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2112 0xF3, 0x0F, 0x10, 0x45, 0x00,
2113 0xF3, 0x0F, 0x10, 0xC1,
2114 0xF3, 0x0F, 0x11, 0x7C, 0x45, 0xF0,
2115 0xF3, 0x44, 0x0F, 0x11, 0x44, 0x45, 0xF0,
2116 0xF3, 0x45, 0x0F, 0x10, 0xF9,
2117 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
2118 0xF2, 0x0F, 0x11, 0x45, 0xF8,
2119 0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0,
2120 0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0,
2121 0x66, 0x0F, 0x2E, 0x45, 0xF0,
2122 0xDD, 0x4D, 0xF0,
2123 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,
2124 0xDB, 0x08,
2125 0xDB, 0x6D, 0x00,
2126 0xD9, 0x45, 0x00,
2127 0x80, 0xF3, 0xFF,
2128 0x0F, 0x0B,
2129 0x48, 0x83, 0xC4, 0xFF,
2130 0x48, 0x81, 0xC4, 0xFF, 0x00, 0x00, 0x00,
2131 0x40, 0x8A, 0x34, 0x08,
2132 };
2133 // zig fmt: on
2134
2135 var as = Assembler.init(input);
2136 var output = std.ArrayList(u8).init(testing.allocator);
2137 defer output.deinit();
2138 try as.assemble(output.writer());
2139 try expectEqualHexStrings(expected, output.items, input);
2140}
2141
2142test "assemble - Jcc" {
2143 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2144 .{ .ja, 0x87 },
2145 .{ .jae, 0x83 },
2146 .{ .jb, 0x82 },
2147 .{ .jbe, 0x86 },
2148 .{ .jc, 0x82 },
2149 .{ .je, 0x84 },
2150 .{ .jg, 0x8f },
2151 .{ .jge, 0x8d },
2152 .{ .jl, 0x8c },
2153 .{ .jle, 0x8e },
2154 .{ .jna, 0x86 },
2155 .{ .jnae, 0x82 },
2156 .{ .jnb, 0x83 },
2157 .{ .jnbe, 0x87 },
2158 .{ .jnc, 0x83 },
2159 .{ .jne, 0x85 },
2160 .{ .jng, 0x8e },
2161 .{ .jnge, 0x8c },
2162 .{ .jnl, 0x8d },
2163 .{ .jnle, 0x8f },
2164 .{ .jno, 0x81 },
2165 .{ .jnp, 0x8b },
2166 .{ .jns, 0x89 },
2167 .{ .jnz, 0x85 },
2168 .{ .jo, 0x80 },
2169 .{ .jp, 0x8a },
2170 .{ .jpe, 0x8a },
2171 .{ .jpo, 0x8b },
2172 .{ .js, 0x88 },
2173 .{ .jz, 0x84 },
2174 };
2175
2176 inline for (&mnemonics) |mnemonic| {
2177 const input = @tagName(mnemonic[0]) ++ " 0x0";
2178 const expected = [_]u8{ 0x0f, mnemonic[1], 0x0, 0x0, 0x0, 0x0 };
2179 var as = Assembler.init(input);
2180 var output = std.ArrayList(u8).init(testing.allocator);
2181 defer output.deinit();
2182 try as.assemble(output.writer());
2183 try expectEqualHexStrings(&expected, output.items, input);
2184 }
2185}
2186
2187test "assemble - SETcc" {
2188 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2189 .{ .seta, 0x97 },
2190 .{ .setae, 0x93 },
2191 .{ .setb, 0x92 },
2192 .{ .setbe, 0x96 },
2193 .{ .setc, 0x92 },
2194 .{ .sete, 0x94 },
2195 .{ .setg, 0x9f },
2196 .{ .setge, 0x9d },
2197 .{ .setl, 0x9c },
2198 .{ .setle, 0x9e },
2199 .{ .setna, 0x96 },
2200 .{ .setnae, 0x92 },
2201 .{ .setnb, 0x93 },
2202 .{ .setnbe, 0x97 },
2203 .{ .setnc, 0x93 },
2204 .{ .setne, 0x95 },
2205 .{ .setng, 0x9e },
2206 .{ .setnge, 0x9c },
2207 .{ .setnl, 0x9d },
2208 .{ .setnle, 0x9f },
2209 .{ .setno, 0x91 },
2210 .{ .setnp, 0x9b },
2211 .{ .setns, 0x99 },
2212 .{ .setnz, 0x95 },
2213 .{ .seto, 0x90 },
2214 .{ .setp, 0x9a },
2215 .{ .setpe, 0x9a },
2216 .{ .setpo, 0x9b },
2217 .{ .sets, 0x98 },
2218 .{ .setz, 0x94 },
2219 };
2220
2221 inline for (&mnemonics) |mnemonic| {
2222 const input = @tagName(mnemonic[0]) ++ " al";
2223 const expected = [_]u8{ 0x0f, mnemonic[1], 0xC0 };
2224 var as = Assembler.init(input);
2225 var output = std.ArrayList(u8).init(testing.allocator);
2226 defer output.deinit();
2227 try as.assemble(output.writer());
2228 try expectEqualHexStrings(&expected, output.items, input);
2229 }
2230}
2231
2232test "assemble - CMOVcc" {
2233 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2234 .{ .cmova, 0x47 },
2235 .{ .cmovae, 0x43 },
2236 .{ .cmovb, 0x42 },
2237 .{ .cmovbe, 0x46 },
2238 .{ .cmovc, 0x42 },
2239 .{ .cmove, 0x44 },
2240 .{ .cmovg, 0x4f },
2241 .{ .cmovge, 0x4d },
2242 .{ .cmovl, 0x4c },
2243 .{ .cmovle, 0x4e },
2244 .{ .cmovna, 0x46 },
2245 .{ .cmovnae, 0x42 },
2246 .{ .cmovnb, 0x43 },
2247 .{ .cmovnbe, 0x47 },
2248 .{ .cmovnc, 0x43 },
2249 .{ .cmovne, 0x45 },
2250 .{ .cmovng, 0x4e },
2251 .{ .cmovnge, 0x4c },
2252 .{ .cmovnl, 0x4d },
2253 .{ .cmovnle, 0x4f },
2254 .{ .cmovno, 0x41 },
2255 .{ .cmovnp, 0x4b },
2256 .{ .cmovns, 0x49 },
2257 .{ .cmovnz, 0x45 },
2258 .{ .cmovo, 0x40 },
2259 .{ .cmovp, 0x4a },
2260 .{ .cmovpe, 0x4a },
2261 .{ .cmovpo, 0x4b },
2262 .{ .cmovs, 0x48 },
2263 .{ .cmovz, 0x44 },
2264 };
2265
2266 inline for (&mnemonics) |mnemonic| {
2267 const input = @tagName(mnemonic[0]) ++ " rax, rbx";
2268 const expected = [_]u8{ 0x48, 0x0f, mnemonic[1], 0xC3 };
2269 var as = Assembler.init(input);
2270 var output = std.ArrayList(u8).init(testing.allocator);
2271 defer output.deinit();
2272 try as.assemble(output.writer());
2273 try expectEqualHexStrings(&expected, output.items, input);
2274 }
2275}