| ... | @@ -0,0 +1,240 @@ |
| 1 | const std = @import("std"); |
| 2 | const DW = std.dwarf; |
| 3 | const testing = std.testing; |
| 4 | |
| 5 | // zig fmt: off |
| 6 | |
| 7 | /// General purpose registers in the AArch64 instruction set |
| 8 | pub const Register = enum(u6) { |
| 9 | // 64-bit registers |
| 10 | x0, x1, x2, x3, x4, x5, x6, x7, |
| 11 | x8, x9, x10, x11, x12, x13, x14, x15, |
| 12 | x16, x17, x18, x19, x20, x21, x22, x23, |
| 13 | x24, x25, x26, x27, x28, x29, x30, xzr, |
| 14 | |
| 15 | // 32-bit registers |
| 16 | w0, w1, w2, w3, w4, w5, w6, w7, |
| 17 | w8, w9, w10, w11, w12, w13, w14, w15, |
| 18 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 19 | w24, w25, w26, w27, w28, w29, w30, wzr, |
| 20 | |
| 21 | pub fn id(self: Register) u5 { |
| 22 | return @truncate(u5, @enumToInt(self)); |
| 23 | } |
| 24 | |
| 25 | /// Returns the bit-width of the register. |
| 26 | pub fn size(self: Register) u7 { |
| 27 | return switch (@enumToInt(self)) { |
| 28 | 0...31 => 64, |
| 29 | 32...63 => 32, |
| 30 | }; |
| 31 | } |
| 32 | |
| 33 | /// Convert from any register to its 64 bit alias. |
| 34 | pub fn to64(self: Register) Register { |
| 35 | return @intToEnum(Register, self.id()); |
| 36 | } |
| 37 | |
| 38 | /// Convert from any register to its 32 bit alias. |
| 39 | pub fn to32(self: Register) Register { |
| 40 | return @intToEnum(Register, @as(u6, self.id()) + 32); |
| 41 | } |
| 42 | |
| 43 | /// Returns the index into `callee_preserved_regs`. |
| 44 | pub fn allocIndex(self: Register) ?u4 { |
| 45 | inline for (callee_preserved_regs) |cpreg, i| { |
| 46 | if (self.id() == cpreg.id()) return i; |
| 47 | } |
| 48 | return null; |
| 49 | } |
| 50 | |
| 51 | pub fn dwarfLocOp(self: Register) u8 { |
| 52 | return @as(u8, self.id()) + DW.OP_reg0; |
| 53 | } |
| 54 | }; |
| 55 | |
| 56 | // zig fmt: on |
| 57 | |
| 58 | pub const callee_preserved_regs = [_]Register{ |
| 59 | .x19, .x20, .x21, .x22, .x23, |
| 60 | .x24, .x25, .x26, .x27, .x28, |
| 61 | }; |
| 62 | |
| 63 | test "Register.id" { |
| 64 | testing.expectEqual(@as(u5, 0), Register.x0.id()); |
| 65 | testing.expectEqual(@as(u5, 0), Register.w0.id()); |
| 66 | |
| 67 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); |
| 68 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); |
| 69 | } |
| 70 | |
| 71 | test "Register.size" { |
| 72 | testing.expectEqual(@as(u7, 64), Register.x19.size()); |
| 73 | testing.expectEqual(@as(u7, 32), Register.w3.size()); |
| 74 | } |
| 75 | |
| 76 | test "Register.to64/to32" { |
| 77 | testing.expectEqual(Register.x0, Register.w0.to64()); |
| 78 | testing.expectEqual(Register.x0, Register.x0.to64()); |
| 79 | |
| 80 | testing.expectEqual(Register.w3, Register.w3.to32()); |
| 81 | testing.expectEqual(Register.w3, Register.x3.to32()); |
| 82 | } |
| 83 | |
| 84 | // zig fmt: off |
| 85 | |
| 86 | /// Scalar floating point registers in the aarch64 instruction set |
| 87 | pub const FloatingPointRegister = enum(u8) { |
| 88 | // 128-bit registers |
| 89 | q0, q1, q2, q3, q4, q5, q6, q7, |
| 90 | q8, q9, q10, q11, q12, q13, q14, q15, |
| 91 | q16, q17, q18, q19, q20, q21, q22, q23, |
| 92 | q24, q25, q26, q27, q28, q29, q30, q31, |
| 93 | |
| 94 | // 64-bit registers |
| 95 | d0, d1, d2, d3, d4, d5, d6, d7, |
| 96 | d8, d9, d10, d11, d12, d13, d14, d15, |
| 97 | d16, d17, d18, d19, d20, d21, d22, d23, |
| 98 | d24, d25, d26, d27, d28, d29, d30, d31, |
| 99 | |
| 100 | // 32-bit registers |
| 101 | s0, s1, s2, s3, s4, s5, s6, s7, |
| 102 | s8, s9, s10, s11, s12, s13, s14, s15, |
| 103 | s16, s17, s18, s19, s20, s21, s22, s23, |
| 104 | s24, s25, s26, s27, s28, s29, s30, s31, |
| 105 | |
| 106 | // 16-bit registers |
| 107 | h0, h1, h2, h3, h4, h5, h6, h7, |
| 108 | h8, h9, h10, h11, h12, h13, h14, h15, |
| 109 | h16, h17, h18, h19, h20, h21, h22, h23, |
| 110 | h24, h25, h26, h27, h28, h29, h30, h31, |
| 111 | |
| 112 | // 8-bit registers |
| 113 | b0, b1, b2, b3, b4, b5, b6, b7, |
| 114 | b8, b9, b10, b11, b12, b13, b14, b15, |
| 115 | b16, b17, b18, b19, b20, b21, b22, b23, |
| 116 | b24, b25, b26, b27, b28, b29, b30, b31, |
| 117 | |
| 118 | pub fn id(self: FloatingPointRegister) u5 { |
| 119 | return @truncate(u5, @enumToInt(self)); |
| 120 | } |
| 121 | |
| 122 | /// Returns the bit-width of the register. |
| 123 | pub fn size(self: FloatingPointRegister) u8 { |
| 124 | return switch (@enumToInt(self)) { |
| 125 | 0...31 => 128, |
| 126 | 32...63 => 64, |
| 127 | 64...95 => 32, |
| 128 | 96...127 => 16, |
| 129 | 128...159 => 8, |
| 130 | else => unreachable, |
| 131 | }; |
| 132 | } |
| 133 | |
| 134 | /// Convert from any register to its 128 bit alias. |
| 135 | pub fn to128(self: FloatingPointRegister) FloatingPointRegister { |
| 136 | return @intToEnum(FloatingPointRegister, self.id()); |
| 137 | } |
| 138 | |
| 139 | /// Convert from any register to its 64 bit alias. |
| 140 | pub fn to64(self: FloatingPointRegister) FloatingPointRegister { |
| 141 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); |
| 142 | } |
| 143 | |
| 144 | /// Convert from any register to its 32 bit alias. |
| 145 | pub fn to32(self: FloatingPointRegister) FloatingPointRegister { |
| 146 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); |
| 147 | } |
| 148 | |
| 149 | /// Convert from any register to its 16 bit alias. |
| 150 | pub fn to16(self: FloatingPointRegister) FloatingPointRegister { |
| 151 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); |
| 152 | } |
| 153 | |
| 154 | /// Convert from any register to its 8 bit alias. |
| 155 | pub fn to8(self: FloatingPointRegister) FloatingPointRegister { |
| 156 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); |
| 157 | } |
| 158 | }; |
| 159 | |
| 160 | // zig fmt: on |
| 161 | |
| 162 | test "FloatingPointRegister.id" { |
| 163 | testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); |
| 164 | testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); |
| 165 | testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); |
| 166 | testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); |
| 167 | testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); |
| 168 | |
| 169 | testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); |
| 170 | testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); |
| 171 | } |
| 172 | |
| 173 | test "FloatingPointRegister.size" { |
| 174 | testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); |
| 175 | testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); |
| 176 | testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); |
| 177 | testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); |
| 178 | testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); |
| 179 | } |
| 180 | |
| 181 | test "FloatingPointRegister.toX" { |
| 182 | testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); |
| 183 | testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); |
| 184 | testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); |
| 185 | |
| 186 | testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); |
| 187 | testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); |
| 188 | testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); |
| 189 | testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); |
| 190 | } |
| 191 | |
| 192 | /// Represents an instruction in the AArch64 instruction set |
| 193 | pub const Instruction = union(enum) { |
| 194 | SupervisorCall: packed struct { |
| 195 | fixed_1: u5 = 0b00001, |
| 196 | imm16: u16, |
| 197 | fixed_2: u11 = 0b11010100000, |
| 198 | }, |
| 199 | |
| 200 | pub fn toU32(self: Instruction) u32 { |
| 201 | return switch (self) { |
| 202 | .SupervisorCall => |v| @bitCast(u32, v), |
| 203 | }; |
| 204 | } |
| 205 | |
| 206 | // Helper functions for assembly syntax functions |
| 207 | |
| 208 | fn supervisorCall(imm16: u16) Instruction { |
| 209 | return Instruction{ |
| 210 | .SupervisorCall = .{ |
| 211 | .imm16 = imm16, |
| 212 | }, |
| 213 | }; |
| 214 | } |
| 215 | |
| 216 | // Supervisor Call |
| 217 | |
| 218 | fn svc(imm16: u16) Instruction { |
| 219 | return supervisorCall(imm16); |
| 220 | } |
| 221 | }; |
| 222 | |
| 223 | test "serialize instructions" { |
| 224 | const Testcase = struct { |
| 225 | inst: Instruction, |
| 226 | expected: u32, |
| 227 | }; |
| 228 | |
| 229 | const testcases = [_]Testcase{ |
| 230 | .{ // svc #0 |
| 231 | .inst = Instruction.svc(0), |
| 232 | .expected = 0b1101_0100_000_0000000000000000_00001, |
| 233 | }, |
| 234 | }; |
| 235 | |
| 236 | for (testcases) |case| { |
| 237 | const actual = case.inst.toU32(); |
| 238 | testing.expectEqual(case.expected, actual); |
| 239 | } |
| 240 | } |