| author | |
| committer | |
| log | 8f58e2d77951cdb046e365394c5f02c9b3a93a4f |
| tree | 3530477de6cda55258f5967d3fba0b352938942f |
| parent | 664941bf14cad3e62b453f83153ca4b65606707b |
14 files changed, 3978 insertions(+), 3978 deletions(-)
CMakeLists.txt+4-4| ... | @@ -551,18 +551,18 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -551,18 +551,18 @@ set(ZIG_STAGE2_SOURCES |
| 551 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" | 551 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 552 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | 552 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 553 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" | 553 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" |
| 554 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig" | ||
| 555 | "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig" | ||
| 556 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig" | ||
| 557 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" | ||
| 554 | "${CMAKE_SOURCE_DIR}/src/clang.zig" | 558 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 555 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" | 559 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 556 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 560 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| 557 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" | 561 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" |
| 558 | "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig" | ||
| 559 | "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig" | ||
| 560 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" | 562 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" |
| 561 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" | 563 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" |
| 562 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" | 564 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" |
| 563 | "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig" | ||
| 564 | "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig" | 565 | "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig" |
| 565 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" | ||
| 566 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" | 566 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 567 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" | 567 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 568 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" | 568 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" |
src/arch/aarch64/bits.zig created+1230| ... | @@ -0,0 +1,1230 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | // zig fmt: off | ||
| 7 | |||
| 8 | /// General purpose registers in the AArch64 instruction set | ||
| 9 | pub const Register = enum(u6) { | ||
| 10 | // 64-bit registers | ||
| 11 | x0, x1, x2, x3, x4, x5, x6, x7, | ||
| 12 | x8, x9, x10, x11, x12, x13, x14, x15, | ||
| 13 | x16, x17, x18, x19, x20, x21, x22, x23, | ||
| 14 | x24, x25, x26, x27, x28, x29, x30, xzr, | ||
| 15 | |||
| 16 | // 32-bit registers | ||
| 17 | w0, w1, w2, w3, w4, w5, w6, w7, | ||
| 18 | w8, w9, w10, w11, w12, w13, w14, w15, | ||
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | ||
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | ||
| 21 | |||
| 22 | pub const sp = Register.xzr; | ||
| 23 | |||
| 24 | pub fn id(self: Register) u5 { | ||
| 25 | return @truncate(u5, @enumToInt(self)); | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Returns the bit-width of the register. | ||
| 29 | pub fn size(self: Register) u7 { | ||
| 30 | return switch (@enumToInt(self)) { | ||
| 31 | 0...31 => 64, | ||
| 32 | 32...63 => 32, | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Convert from any register to its 64 bit alias. | ||
| 37 | pub fn to64(self: Register) Register { | ||
| 38 | return @intToEnum(Register, self.id()); | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Convert from any register to its 32 bit alias. | ||
| 42 | pub fn to32(self: Register) Register { | ||
| 43 | return @intToEnum(Register, @as(u6, self.id()) + 32); | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Returns the index into `callee_preserved_regs`. | ||
| 47 | pub fn allocIndex(self: Register) ?u4 { | ||
| 48 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 49 | if (self.id() == cpreg.id()) return i; | ||
| 50 | } | ||
| 51 | return null; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 55 | return @as(u8, self.id()) + DW.OP.reg0; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | // zig fmt: on | ||
| 60 | |||
| 61 | pub const callee_preserved_regs = [_]Register{ | ||
| 62 | .x19, .x20, .x21, .x22, .x23, | ||
| 63 | .x24, .x25, .x26, .x27, .x28, | ||
| 64 | }; | ||
| 65 | |||
| 66 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 67 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 68 | |||
| 69 | test "Register.id" { | ||
| 70 | try testing.expectEqual(@as(u5, 0), Register.x0.id()); | ||
| 71 | try testing.expectEqual(@as(u5, 0), Register.w0.id()); | ||
| 72 | |||
| 73 | try testing.expectEqual(@as(u5, 31), Register.xzr.id()); | ||
| 74 | try testing.expectEqual(@as(u5, 31), Register.wzr.id()); | ||
| 75 | |||
| 76 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | ||
| 77 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | ||
| 78 | } | ||
| 79 | |||
| 80 | test "Register.size" { | ||
| 81 | try testing.expectEqual(@as(u7, 64), Register.x19.size()); | ||
| 82 | try testing.expectEqual(@as(u7, 32), Register.w3.size()); | ||
| 83 | } | ||
| 84 | |||
| 85 | test "Register.to64/to32" { | ||
| 86 | try testing.expectEqual(Register.x0, Register.w0.to64()); | ||
| 87 | try testing.expectEqual(Register.x0, Register.x0.to64()); | ||
| 88 | |||
| 89 | try testing.expectEqual(Register.w3, Register.w3.to32()); | ||
| 90 | try testing.expectEqual(Register.w3, Register.x3.to32()); | ||
| 91 | } | ||
| 92 | |||
| 93 | // zig fmt: off | ||
| 94 | |||
| 95 | /// Scalar floating point registers in the aarch64 instruction set | ||
| 96 | pub const FloatingPointRegister = enum(u8) { | ||
| 97 | // 128-bit registers | ||
| 98 | q0, q1, q2, q3, q4, q5, q6, q7, | ||
| 99 | q8, q9, q10, q11, q12, q13, q14, q15, | ||
| 100 | q16, q17, q18, q19, q20, q21, q22, q23, | ||
| 101 | q24, q25, q26, q27, q28, q29, q30, q31, | ||
| 102 | |||
| 103 | // 64-bit registers | ||
| 104 | d0, d1, d2, d3, d4, d5, d6, d7, | ||
| 105 | d8, d9, d10, d11, d12, d13, d14, d15, | ||
| 106 | d16, d17, d18, d19, d20, d21, d22, d23, | ||
| 107 | d24, d25, d26, d27, d28, d29, d30, d31, | ||
| 108 | |||
| 109 | // 32-bit registers | ||
| 110 | s0, s1, s2, s3, s4, s5, s6, s7, | ||
| 111 | s8, s9, s10, s11, s12, s13, s14, s15, | ||
| 112 | s16, s17, s18, s19, s20, s21, s22, s23, | ||
| 113 | s24, s25, s26, s27, s28, s29, s30, s31, | ||
| 114 | |||
| 115 | // 16-bit registers | ||
| 116 | h0, h1, h2, h3, h4, h5, h6, h7, | ||
| 117 | h8, h9, h10, h11, h12, h13, h14, h15, | ||
| 118 | h16, h17, h18, h19, h20, h21, h22, h23, | ||
| 119 | h24, h25, h26, h27, h28, h29, h30, h31, | ||
| 120 | |||
| 121 | // 8-bit registers | ||
| 122 | b0, b1, b2, b3, b4, b5, b6, b7, | ||
| 123 | b8, b9, b10, b11, b12, b13, b14, b15, | ||
| 124 | b16, b17, b18, b19, b20, b21, b22, b23, | ||
| 125 | b24, b25, b26, b27, b28, b29, b30, b31, | ||
| 126 | |||
| 127 | pub fn id(self: FloatingPointRegister) u5 { | ||
| 128 | return @truncate(u5, @enumToInt(self)); | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Returns the bit-width of the register. | ||
| 132 | pub fn size(self: FloatingPointRegister) u8 { | ||
| 133 | return switch (@enumToInt(self)) { | ||
| 134 | 0...31 => 128, | ||
| 135 | 32...63 => 64, | ||
| 136 | 64...95 => 32, | ||
| 137 | 96...127 => 16, | ||
| 138 | 128...159 => 8, | ||
| 139 | else => unreachable, | ||
| 140 | }; | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Convert from any register to its 128 bit alias. | ||
| 144 | pub fn to128(self: FloatingPointRegister) FloatingPointRegister { | ||
| 145 | return @intToEnum(FloatingPointRegister, self.id()); | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Convert from any register to its 64 bit alias. | ||
| 149 | pub fn to64(self: FloatingPointRegister) FloatingPointRegister { | ||
| 150 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Convert from any register to its 32 bit alias. | ||
| 154 | pub fn to32(self: FloatingPointRegister) FloatingPointRegister { | ||
| 155 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Convert from any register to its 16 bit alias. | ||
| 159 | pub fn to16(self: FloatingPointRegister) FloatingPointRegister { | ||
| 160 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Convert from any register to its 8 bit alias. | ||
| 164 | pub fn to8(self: FloatingPointRegister) FloatingPointRegister { | ||
| 165 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 169 | // zig fmt: on | ||
| 170 | |||
| 171 | test "FloatingPointRegister.id" { | ||
| 172 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); | ||
| 173 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); | ||
| 174 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); | ||
| 175 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); | ||
| 176 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); | ||
| 177 | |||
| 178 | try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); | ||
| 179 | try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); | ||
| 180 | } | ||
| 181 | |||
| 182 | test "FloatingPointRegister.size" { | ||
| 183 | try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); | ||
| 184 | try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); | ||
| 185 | try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); | ||
| 186 | try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); | ||
| 187 | try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); | ||
| 188 | } | ||
| 189 | |||
| 190 | test "FloatingPointRegister.toX" { | ||
| 191 | try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); | ||
| 192 | try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); | ||
| 193 | try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); | ||
| 194 | |||
| 195 | try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); | ||
| 196 | try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); | ||
| 197 | try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); | ||
| 198 | try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Represents an instruction in the AArch64 instruction set | ||
| 202 | pub const Instruction = union(enum) { | ||
| 203 | move_wide_immediate: packed struct { | ||
| 204 | rd: u5, | ||
| 205 | imm16: u16, | ||
| 206 | hw: u2, | ||
| 207 | fixed: u6 = 0b100101, | ||
| 208 | opc: u2, | ||
| 209 | sf: u1, | ||
| 210 | }, | ||
| 211 | pc_relative_address: packed struct { | ||
| 212 | rd: u5, | ||
| 213 | immhi: u19, | ||
| 214 | fixed: u5 = 0b10000, | ||
| 215 | immlo: u2, | ||
| 216 | op: u1, | ||
| 217 | }, | ||
| 218 | load_store_register: packed struct { | ||
| 219 | rt: u5, | ||
| 220 | rn: u5, | ||
| 221 | offset: u12, | ||
| 222 | opc: u2, | ||
| 223 | op1: u2, | ||
| 224 | v: u1, | ||
| 225 | fixed: u3 = 0b111, | ||
| 226 | size: u2, | ||
| 227 | }, | ||
| 228 | load_store_register_pair: packed struct { | ||
| 229 | rt1: u5, | ||
| 230 | rn: u5, | ||
| 231 | rt2: u5, | ||
| 232 | imm7: u7, | ||
| 233 | load: u1, | ||
| 234 | encoding: u2, | ||
| 235 | fixed: u5 = 0b101_0_0, | ||
| 236 | opc: u2, | ||
| 237 | }, | ||
| 238 | load_literal: packed struct { | ||
| 239 | rt: u5, | ||
| 240 | imm19: u19, | ||
| 241 | fixed: u6 = 0b011_0_00, | ||
| 242 | opc: u2, | ||
| 243 | }, | ||
| 244 | exception_generation: packed struct { | ||
| 245 | ll: u2, | ||
| 246 | op2: u3, | ||
| 247 | imm16: u16, | ||
| 248 | opc: u3, | ||
| 249 | fixed: u8 = 0b1101_0100, | ||
| 250 | }, | ||
| 251 | unconditional_branch_register: packed struct { | ||
| 252 | op4: u5, | ||
| 253 | rn: u5, | ||
| 254 | op3: u6, | ||
| 255 | op2: u5, | ||
| 256 | opc: u4, | ||
| 257 | fixed: u7 = 0b1101_011, | ||
| 258 | }, | ||
| 259 | unconditional_branch_immediate: packed struct { | ||
| 260 | imm26: u26, | ||
| 261 | fixed: u5 = 0b00101, | ||
| 262 | op: u1, | ||
| 263 | }, | ||
| 264 | no_operation: packed struct { | ||
| 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, | ||
| 266 | }, | ||
| 267 | logical_shifted_register: packed struct { | ||
| 268 | rd: u5, | ||
| 269 | rn: u5, | ||
| 270 | imm6: u6, | ||
| 271 | rm: u5, | ||
| 272 | n: u1, | ||
| 273 | shift: u2, | ||
| 274 | fixed: u5 = 0b01010, | ||
| 275 | opc: u2, | ||
| 276 | sf: u1, | ||
| 277 | }, | ||
| 278 | add_subtract_immediate: packed struct { | ||
| 279 | rd: u5, | ||
| 280 | rn: u5, | ||
| 281 | imm12: u12, | ||
| 282 | sh: u1, | ||
| 283 | fixed: u6 = 0b100010, | ||
| 284 | s: u1, | ||
| 285 | op: u1, | ||
| 286 | sf: u1, | ||
| 287 | }, | ||
| 288 | conditional_branch: struct { | ||
| 289 | cond: u4, | ||
| 290 | o0: u1, | ||
| 291 | imm19: u19, | ||
| 292 | o1: u1, | ||
| 293 | fixed: u7 = 0b0101010, | ||
| 294 | }, | ||
| 295 | compare_and_branch: struct { | ||
| 296 | rt: u5, | ||
| 297 | imm19: u19, | ||
| 298 | op: u1, | ||
| 299 | fixed: u6 = 0b011010, | ||
| 300 | sf: u1, | ||
| 301 | }, | ||
| 302 | |||
| 303 | pub const Shift = struct { | ||
| 304 | shift: Type = .lsl, | ||
| 305 | amount: u6 = 0, | ||
| 306 | |||
| 307 | pub const Type = enum(u2) { | ||
| 308 | lsl, | ||
| 309 | lsr, | ||
| 310 | asr, | ||
| 311 | ror, | ||
| 312 | }; | ||
| 313 | |||
| 314 | pub const none = Shift{ | ||
| 315 | .shift = .lsl, | ||
| 316 | .amount = 0, | ||
| 317 | }; | ||
| 318 | }; | ||
| 319 | |||
| 320 | pub const Condition = enum(u4) { | ||
| 321 | /// Integer: Equal | ||
| 322 | /// Floating point: Equal | ||
| 323 | eq, | ||
| 324 | /// Integer: Not equal | ||
| 325 | /// Floating point: Not equal or unordered | ||
| 326 | ne, | ||
| 327 | /// Integer: Carry set | ||
| 328 | /// Floating point: Greater than, equal, or unordered | ||
| 329 | cs, | ||
| 330 | /// Integer: Carry clear | ||
| 331 | /// Floating point: Less than | ||
| 332 | cc, | ||
| 333 | /// Integer: Minus, negative | ||
| 334 | /// Floating point: Less than | ||
| 335 | mi, | ||
| 336 | /// Integer: Plus, positive or zero | ||
| 337 | /// Floating point: Greater than, equal, or unordered | ||
| 338 | pl, | ||
| 339 | /// Integer: Overflow | ||
| 340 | /// Floating point: Unordered | ||
| 341 | vs, | ||
| 342 | /// Integer: No overflow | ||
| 343 | /// Floating point: Ordered | ||
| 344 | vc, | ||
| 345 | /// Integer: Unsigned higher | ||
| 346 | /// Floating point: Greater than, or unordered | ||
| 347 | hi, | ||
| 348 | /// Integer: Unsigned lower or same | ||
| 349 | /// Floating point: Less than or equal | ||
| 350 | ls, | ||
| 351 | /// Integer: Signed greater than or equal | ||
| 352 | /// Floating point: Greater than or equal | ||
| 353 | ge, | ||
| 354 | /// Integer: Signed less than | ||
| 355 | /// Floating point: Less than, or unordered | ||
| 356 | lt, | ||
| 357 | /// Integer: Signed greater than | ||
| 358 | /// Floating point: Greater than | ||
| 359 | gt, | ||
| 360 | /// Integer: Signed less than or equal | ||
| 361 | /// Floating point: Less than, equal, or unordered | ||
| 362 | le, | ||
| 363 | /// Integer: Always | ||
| 364 | /// Floating point: Always | ||
| 365 | al, | ||
| 366 | /// Integer: Always | ||
| 367 | /// Floating point: Always | ||
| 368 | nv, | ||
| 369 | }; | ||
| 370 | |||
| 371 | pub fn toU32(self: Instruction) u32 { | ||
| 372 | return switch (self) { | ||
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), | ||
| 374 | .pc_relative_address => |v| @bitCast(u32, v), | ||
| 375 | .load_store_register => |v| @bitCast(u32, v), | ||
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), | ||
| 377 | .load_literal => |v| @bitCast(u32, v), | ||
| 378 | .exception_generation => |v| @bitCast(u32, v), | ||
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), | ||
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), | ||
| 381 | .no_operation => |v| @bitCast(u32, v), | ||
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), | ||
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), | ||
| 384 | // TODO once packed structs work, this can be refactored | ||
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), | ||
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), | ||
| 387 | }; | ||
| 388 | } | ||
| 389 | |||
| 390 | fn moveWideImmediate( | ||
| 391 | opc: u2, | ||
| 392 | rd: Register, | ||
| 393 | imm16: u16, | ||
| 394 | shift: u6, | ||
| 395 | ) Instruction { | ||
| 396 | switch (rd.size()) { | ||
| 397 | 32 => { | ||
| 398 | assert(shift % 16 == 0 and shift <= 16); | ||
| 399 | return Instruction{ | ||
| 400 | .move_wide_immediate = .{ | ||
| 401 | .rd = rd.id(), | ||
| 402 | .imm16 = imm16, | ||
| 403 | .hw = @intCast(u2, shift / 16), | ||
| 404 | .opc = opc, | ||
| 405 | .sf = 0, | ||
| 406 | }, | ||
| 407 | }; | ||
| 408 | }, | ||
| 409 | 64 => { | ||
| 410 | assert(shift % 16 == 0 and shift <= 48); | ||
| 411 | return Instruction{ | ||
| 412 | .move_wide_immediate = .{ | ||
| 413 | .rd = rd.id(), | ||
| 414 | .imm16 = imm16, | ||
| 415 | .hw = @intCast(u2, shift / 16), | ||
| 416 | .opc = opc, | ||
| 417 | .sf = 1, | ||
| 418 | }, | ||
| 419 | }; | ||
| 420 | }, | ||
| 421 | else => unreachable, // unexpected register size | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { | ||
| 426 | assert(rd.size() == 64); | ||
| 427 | const imm21_u = @bitCast(u21, imm21); | ||
| 428 | return Instruction{ | ||
| 429 | .pc_relative_address = .{ | ||
| 430 | .rd = rd.id(), | ||
| 431 | .immlo = @truncate(u2, imm21_u), | ||
| 432 | .immhi = @truncate(u19, imm21_u >> 2), | ||
| 433 | .op = op, | ||
| 434 | }, | ||
| 435 | }; | ||
| 436 | } | ||
| 437 | |||
| 438 | /// Represents the offset operand of a load or store instruction. | ||
| 439 | /// Data can be loaded from memory with either an immediate offset | ||
| 440 | /// or an offset that is stored in some register. | ||
| 441 | pub const LoadStoreOffset = union(enum) { | ||
| 442 | Immediate: union(enum) { | ||
| 443 | PostIndex: i9, | ||
| 444 | PreIndex: i9, | ||
| 445 | Unsigned: u12, | ||
| 446 | }, | ||
| 447 | Register: struct { | ||
| 448 | rm: u5, | ||
| 449 | shift: union(enum) { | ||
| 450 | Uxtw: u2, | ||
| 451 | Lsl: u2, | ||
| 452 | Sxtw: u2, | ||
| 453 | Sxtx: u2, | ||
| 454 | }, | ||
| 455 | }, | ||
| 456 | |||
| 457 | pub const none = LoadStoreOffset{ | ||
| 458 | .Immediate = .{ .Unsigned = 0 }, | ||
| 459 | }; | ||
| 460 | |||
| 461 | pub fn toU12(self: LoadStoreOffset) u12 { | ||
| 462 | return switch (self) { | ||
| 463 | .Immediate => |imm_type| switch (imm_type) { | ||
| 464 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, | ||
| 465 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, | ||
| 466 | .Unsigned => |v| v, | ||
| 467 | }, | ||
| 468 | .Register => |r| switch (r.shift) { | ||
| 469 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, | ||
| 470 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, | ||
| 471 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, | ||
| 472 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, | ||
| 473 | }, | ||
| 474 | }; | ||
| 475 | } | ||
| 476 | |||
| 477 | pub fn imm(offset: u12) LoadStoreOffset { | ||
| 478 | return .{ | ||
| 479 | .Immediate = .{ .Unsigned = offset }, | ||
| 480 | }; | ||
| 481 | } | ||
| 482 | |||
| 483 | pub fn imm_post_index(offset: i9) LoadStoreOffset { | ||
| 484 | return .{ | ||
| 485 | .Immediate = .{ .PostIndex = offset }, | ||
| 486 | }; | ||
| 487 | } | ||
| 488 | |||
| 489 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { | ||
| 490 | return .{ | ||
| 491 | .Immediate = .{ .PreIndex = offset }, | ||
| 492 | }; | ||
| 493 | } | ||
| 494 | |||
| 495 | pub fn reg(rm: Register) LoadStoreOffset { | ||
| 496 | return .{ | ||
| 497 | .Register = .{ | ||
| 498 | .rm = rm.id(), | ||
| 499 | .shift = .{ | ||
| 500 | .Lsl = 0, | ||
| 501 | }, | ||
| 502 | }, | ||
| 503 | }; | ||
| 504 | } | ||
| 505 | |||
| 506 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { | ||
| 507 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | ||
| 508 | return .{ | ||
| 509 | .Register = .{ | ||
| 510 | .rm = rm.id(), | ||
| 511 | .shift = .{ | ||
| 512 | .Uxtw = shift, | ||
| 513 | }, | ||
| 514 | }, | ||
| 515 | }; | ||
| 516 | } | ||
| 517 | |||
| 518 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { | ||
| 519 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | ||
| 520 | return .{ | ||
| 521 | .Register = .{ | ||
| 522 | .rm = rm.id(), | ||
| 523 | .shift = .{ | ||
| 524 | .Lsl = shift, | ||
| 525 | }, | ||
| 526 | }, | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | |||
| 530 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { | ||
| 531 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | ||
| 532 | return .{ | ||
| 533 | .Register = .{ | ||
| 534 | .rm = rm.id(), | ||
| 535 | .shift = .{ | ||
| 536 | .Sxtw = shift, | ||
| 537 | }, | ||
| 538 | }, | ||
| 539 | }; | ||
| 540 | } | ||
| 541 | |||
| 542 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { | ||
| 543 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | ||
| 544 | return .{ | ||
| 545 | .Register = .{ | ||
| 546 | .rm = rm.id(), | ||
| 547 | .shift = .{ | ||
| 548 | .Sxtx = shift, | ||
| 549 | }, | ||
| 550 | }, | ||
| 551 | }; | ||
| 552 | } | ||
| 553 | }; | ||
| 554 | |||
| 555 | /// Which kind of load/store to perform | ||
| 556 | const LoadStoreVariant = enum { | ||
| 557 | /// 32-bit or 64-bit | ||
| 558 | str, | ||
| 559 | /// 16-bit, zero-extended | ||
| 560 | strh, | ||
| 561 | /// 8-bit, zero-extended | ||
| 562 | strb, | ||
| 563 | /// 32-bit or 64-bit | ||
| 564 | ldr, | ||
| 565 | /// 16-bit, zero-extended | ||
| 566 | ldrh, | ||
| 567 | /// 8-bit, zero-extended | ||
| 568 | ldrb, | ||
| 569 | }; | ||
| 570 | |||
| 571 | fn loadStoreRegister( | ||
| 572 | rt: Register, | ||
| 573 | rn: Register, | ||
| 574 | offset: LoadStoreOffset, | ||
| 575 | variant: LoadStoreVariant, | ||
| 576 | ) Instruction { | ||
| 577 | const off = offset.toU12(); | ||
| 578 | const op1: u2 = blk: { | ||
| 579 | switch (offset) { | ||
| 580 | .Immediate => |imm| switch (imm) { | ||
| 581 | .Unsigned => break :blk 0b01, | ||
| 582 | else => {}, | ||
| 583 | }, | ||
| 584 | else => {}, | ||
| 585 | } | ||
| 586 | break :blk 0b00; | ||
| 587 | }; | ||
| 588 | const opc: u2 = switch (variant) { | ||
| 589 | .ldr, .ldrh, .ldrb => 0b01, | ||
| 590 | .str, .strh, .strb => 0b00, | ||
| 591 | }; | ||
| 592 | return Instruction{ | ||
| 593 | .load_store_register = .{ | ||
| 594 | .rt = rt.id(), | ||
| 595 | .rn = rn.id(), | ||
| 596 | .offset = off, | ||
| 597 | .opc = opc, | ||
| 598 | .op1 = op1, | ||
| 599 | .v = 0, | ||
| 600 | .size = blk: { | ||
| 601 | switch (variant) { | ||
| 602 | .ldr, .str => switch (rt.size()) { | ||
| 603 | 32 => break :blk 0b10, | ||
| 604 | 64 => break :blk 0b11, | ||
| 605 | else => unreachable, // unexpected register size | ||
| 606 | }, | ||
| 607 | .ldrh, .strh => break :blk 0b01, | ||
| 608 | .ldrb, .strb => break :blk 0b00, | ||
| 609 | } | ||
| 610 | }, | ||
| 611 | }, | ||
| 612 | }; | ||
| 613 | } | ||
| 614 | |||
| 615 | fn loadStoreRegisterPair( | ||
| 616 | rt1: Register, | ||
| 617 | rt2: Register, | ||
| 618 | rn: Register, | ||
| 619 | offset: i9, | ||
| 620 | encoding: u2, | ||
| 621 | load: bool, | ||
| 622 | ) Instruction { | ||
| 623 | switch (rt1.size()) { | ||
| 624 | 32 => { | ||
| 625 | assert(-256 <= offset and offset <= 252); | ||
| 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); | ||
| 627 | return Instruction{ | ||
| 628 | .load_store_register_pair = .{ | ||
| 629 | .rt1 = rt1.id(), | ||
| 630 | .rn = rn.id(), | ||
| 631 | .rt2 = rt2.id(), | ||
| 632 | .imm7 = imm7, | ||
| 633 | .load = @boolToInt(load), | ||
| 634 | .encoding = encoding, | ||
| 635 | .opc = 0b00, | ||
| 636 | }, | ||
| 637 | }; | ||
| 638 | }, | ||
| 639 | 64 => { | ||
| 640 | assert(-512 <= offset and offset <= 504); | ||
| 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); | ||
| 642 | return Instruction{ | ||
| 643 | .load_store_register_pair = .{ | ||
| 644 | .rt1 = rt1.id(), | ||
| 645 | .rn = rn.id(), | ||
| 646 | .rt2 = rt2.id(), | ||
| 647 | .imm7 = imm7, | ||
| 648 | .load = @boolToInt(load), | ||
| 649 | .encoding = encoding, | ||
| 650 | .opc = 0b10, | ||
| 651 | }, | ||
| 652 | }; | ||
| 653 | }, | ||
| 654 | else => unreachable, // unexpected register size | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | ||
| 659 | switch (rt.size()) { | ||
| 660 | 32 => { | ||
| 661 | return Instruction{ | ||
| 662 | .load_literal = .{ | ||
| 663 | .rt = rt.id(), | ||
| 664 | .imm19 = imm19, | ||
| 665 | .opc = 0b00, | ||
| 666 | }, | ||
| 667 | }; | ||
| 668 | }, | ||
| 669 | 64 => { | ||
| 670 | return Instruction{ | ||
| 671 | .load_literal = .{ | ||
| 672 | .rt = rt.id(), | ||
| 673 | .imm19 = imm19, | ||
| 674 | .opc = 0b01, | ||
| 675 | }, | ||
| 676 | }; | ||
| 677 | }, | ||
| 678 | else => unreachable, // unexpected register size | ||
| 679 | } | ||
| 680 | } | ||
| 681 | |||
| 682 | fn exceptionGeneration( | ||
| 683 | opc: u3, | ||
| 684 | op2: u3, | ||
| 685 | ll: u2, | ||
| 686 | imm16: u16, | ||
| 687 | ) Instruction { | ||
| 688 | return Instruction{ | ||
| 689 | .exception_generation = .{ | ||
| 690 | .ll = ll, | ||
| 691 | .op2 = op2, | ||
| 692 | .imm16 = imm16, | ||
| 693 | .opc = opc, | ||
| 694 | }, | ||
| 695 | }; | ||
| 696 | } | ||
| 697 | |||
| 698 | fn unconditionalBranchRegister( | ||
| 699 | opc: u4, | ||
| 700 | op2: u5, | ||
| 701 | op3: u6, | ||
| 702 | rn: Register, | ||
| 703 | op4: u5, | ||
| 704 | ) Instruction { | ||
| 705 | assert(rn.size() == 64); | ||
| 706 | |||
| 707 | return Instruction{ | ||
| 708 | .unconditional_branch_register = .{ | ||
| 709 | .op4 = op4, | ||
| 710 | .rn = rn.id(), | ||
| 711 | .op3 = op3, | ||
| 712 | .op2 = op2, | ||
| 713 | .opc = opc, | ||
| 714 | }, | ||
| 715 | }; | ||
| 716 | } | ||
| 717 | |||
| 718 | fn unconditionalBranchImmediate( | ||
| 719 | op: u1, | ||
| 720 | offset: i28, | ||
| 721 | ) Instruction { | ||
| 722 | return Instruction{ | ||
| 723 | .unconditional_branch_immediate = .{ | ||
| 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), | ||
| 725 | .op = op, | ||
| 726 | }, | ||
| 727 | }; | ||
| 728 | } | ||
| 729 | |||
| 730 | fn logicalShiftedRegister( | ||
| 731 | opc: u2, | ||
| 732 | n: u1, | ||
| 733 | shift: Shift, | ||
| 734 | rd: Register, | ||
| 735 | rn: Register, | ||
| 736 | rm: Register, | ||
| 737 | ) Instruction { | ||
| 738 | switch (rd.size()) { | ||
| 739 | 32 => { | ||
| 740 | assert(shift.amount < 32); | ||
| 741 | return Instruction{ | ||
| 742 | .logical_shifted_register = .{ | ||
| 743 | .rd = rd.id(), | ||
| 744 | .rn = rn.id(), | ||
| 745 | .imm6 = shift.amount, | ||
| 746 | .rm = rm.id(), | ||
| 747 | .n = n, | ||
| 748 | .shift = @enumToInt(shift.shift), | ||
| 749 | .opc = opc, | ||
| 750 | .sf = 0b0, | ||
| 751 | }, | ||
| 752 | }; | ||
| 753 | }, | ||
| 754 | 64 => { | ||
| 755 | return Instruction{ | ||
| 756 | .logical_shifted_register = .{ | ||
| 757 | .rd = rd.id(), | ||
| 758 | .rn = rn.id(), | ||
| 759 | .imm6 = shift.amount, | ||
| 760 | .rm = rm.id(), | ||
| 761 | .n = n, | ||
| 762 | .shift = @enumToInt(shift.shift), | ||
| 763 | .opc = opc, | ||
| 764 | .sf = 0b1, | ||
| 765 | }, | ||
| 766 | }; | ||
| 767 | }, | ||
| 768 | else => unreachable, // unexpected register size | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | fn addSubtractImmediate( | ||
| 773 | op: u1, | ||
| 774 | s: u1, | ||
| 775 | rd: Register, | ||
| 776 | rn: Register, | ||
| 777 | imm12: u12, | ||
| 778 | shift: bool, | ||
| 779 | ) Instruction { | ||
| 780 | return Instruction{ | ||
| 781 | .add_subtract_immediate = .{ | ||
| 782 | .rd = rd.id(), | ||
| 783 | .rn = rn.id(), | ||
| 784 | .imm12 = imm12, | ||
| 785 | .sh = @boolToInt(shift), | ||
| 786 | .s = s, | ||
| 787 | .op = op, | ||
| 788 | .sf = switch (rd.size()) { | ||
| 789 | 32 => 0b0, | ||
| 790 | 64 => 0b1, | ||
| 791 | else => unreachable, // unexpected register size | ||
| 792 | }, | ||
| 793 | }, | ||
| 794 | }; | ||
| 795 | } | ||
| 796 | |||
| 797 | fn conditionalBranch( | ||
| 798 | o0: u1, | ||
| 799 | o1: u1, | ||
| 800 | cond: Condition, | ||
| 801 | offset: i21, | ||
| 802 | ) Instruction { | ||
| 803 | assert(offset & 0b11 == 0b00); | ||
| 804 | return Instruction{ | ||
| 805 | .conditional_branch = .{ | ||
| 806 | .cond = @enumToInt(cond), | ||
| 807 | .o0 = o0, | ||
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | ||
| 809 | .o1 = o1, | ||
| 810 | }, | ||
| 811 | }; | ||
| 812 | } | ||
| 813 | |||
| 814 | fn compareAndBranch( | ||
| 815 | op: u1, | ||
| 816 | rt: Register, | ||
| 817 | offset: i21, | ||
| 818 | ) Instruction { | ||
| 819 | assert(offset & 0b11 == 0b00); | ||
| 820 | return Instruction{ | ||
| 821 | .compare_and_branch = .{ | ||
| 822 | .rt = rt.id(), | ||
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | ||
| 824 | .op = op, | ||
| 825 | .sf = switch (rt.size()) { | ||
| 826 | 32 => 0b0, | ||
| 827 | 64 => 0b1, | ||
| 828 | else => unreachable, // unexpected register size | ||
| 829 | }, | ||
| 830 | }, | ||
| 831 | }; | ||
| 832 | } | ||
| 833 | |||
| 834 | // Helper functions for assembly syntax functions | ||
| 835 | |||
| 836 | // Move wide (immediate) | ||
| 837 | |||
| 838 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 839 | return moveWideImmediate(0b00, rd, imm16, shift); | ||
| 840 | } | ||
| 841 | |||
| 842 | pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 843 | return moveWideImmediate(0b10, rd, imm16, shift); | ||
| 844 | } | ||
| 845 | |||
| 846 | pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 847 | return moveWideImmediate(0b11, rd, imm16, shift); | ||
| 848 | } | ||
| 849 | |||
| 850 | // PC relative address | ||
| 851 | |||
| 852 | pub fn adr(rd: Register, imm21: i21) Instruction { | ||
| 853 | return pcRelativeAddress(rd, imm21, 0b0); | ||
| 854 | } | ||
| 855 | |||
| 856 | pub fn adrp(rd: Register, imm21: i21) Instruction { | ||
| 857 | return pcRelativeAddress(rd, imm21, 0b1); | ||
| 858 | } | ||
| 859 | |||
| 860 | // Load or store register | ||
| 861 | |||
| 862 | pub const LdrArgs = union(enum) { | ||
| 863 | register: struct { | ||
| 864 | rn: Register, | ||
| 865 | offset: LoadStoreOffset = LoadStoreOffset.none, | ||
| 866 | }, | ||
| 867 | literal: u19, | ||
| 868 | }; | ||
| 869 | |||
| 870 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | ||
| 871 | switch (args) { | ||
| 872 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), | ||
| 873 | .literal => |literal| return loadLiteral(rt, literal), | ||
| 874 | } | ||
| 875 | } | ||
| 876 | |||
| 877 | pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 878 | return loadStoreRegister(rt, rn, args.offset, .ldrh); | ||
| 879 | } | ||
| 880 | |||
| 881 | pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 882 | return loadStoreRegister(rt, rn, args.offset, .ldrb); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub const StrArgs = struct { | ||
| 886 | offset: LoadStoreOffset = LoadStoreOffset.none, | ||
| 887 | }; | ||
| 888 | |||
| 889 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 890 | return loadStoreRegister(rt, rn, args.offset, .str); | ||
| 891 | } | ||
| 892 | |||
| 893 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 894 | return loadStoreRegister(rt, rn, args.offset, .strh); | ||
| 895 | } | ||
| 896 | |||
| 897 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 898 | return loadStoreRegister(rt, rn, args.offset, .strb); | ||
| 899 | } | ||
| 900 | |||
| 901 | // Load or store pair of registers | ||
| 902 | |||
| 903 | pub const LoadStorePairOffset = struct { | ||
| 904 | encoding: enum(u2) { | ||
| 905 | PostIndex = 0b01, | ||
| 906 | Signed = 0b10, | ||
| 907 | PreIndex = 0b11, | ||
| 908 | }, | ||
| 909 | offset: i9, | ||
| 910 | |||
| 911 | pub fn none() LoadStorePairOffset { | ||
| 912 | return .{ .encoding = .Signed, .offset = 0 }; | ||
| 913 | } | ||
| 914 | |||
| 915 | pub fn post_index(imm: i9) LoadStorePairOffset { | ||
| 916 | return .{ .encoding = .PostIndex, .offset = imm }; | ||
| 917 | } | ||
| 918 | |||
| 919 | pub fn pre_index(imm: i9) LoadStorePairOffset { | ||
| 920 | return .{ .encoding = .PreIndex, .offset = imm }; | ||
| 921 | } | ||
| 922 | |||
| 923 | pub fn signed(imm: i9) LoadStorePairOffset { | ||
| 924 | return .{ .encoding = .Signed, .offset = imm }; | ||
| 925 | } | ||
| 926 | }; | ||
| 927 | |||
| 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | ||
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | ||
| 930 | } | ||
| 931 | |||
| 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | ||
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); | ||
| 934 | } | ||
| 935 | |||
| 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | ||
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | ||
| 938 | } | ||
| 939 | |||
| 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | ||
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); | ||
| 942 | } | ||
| 943 | |||
| 944 | // Exception generation | ||
| 945 | |||
| 946 | pub fn svc(imm16: u16) Instruction { | ||
| 947 | return exceptionGeneration(0b000, 0b000, 0b01, imm16); | ||
| 948 | } | ||
| 949 | |||
| 950 | pub fn hvc(imm16: u16) Instruction { | ||
| 951 | return exceptionGeneration(0b000, 0b000, 0b10, imm16); | ||
| 952 | } | ||
| 953 | |||
| 954 | pub fn smc(imm16: u16) Instruction { | ||
| 955 | return exceptionGeneration(0b000, 0b000, 0b11, imm16); | ||
| 956 | } | ||
| 957 | |||
| 958 | pub fn brk(imm16: u16) Instruction { | ||
| 959 | return exceptionGeneration(0b001, 0b000, 0b00, imm16); | ||
| 960 | } | ||
| 961 | |||
| 962 | pub fn hlt(imm16: u16) Instruction { | ||
| 963 | return exceptionGeneration(0b010, 0b000, 0b00, imm16); | ||
| 964 | } | ||
| 965 | |||
| 966 | // Unconditional branch (register) | ||
| 967 | |||
| 968 | pub fn br(rn: Register) Instruction { | ||
| 969 | return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); | ||
| 970 | } | ||
| 971 | |||
| 972 | pub fn blr(rn: Register) Instruction { | ||
| 973 | return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); | ||
| 974 | } | ||
| 975 | |||
| 976 | pub fn ret(rn: ?Register) Instruction { | ||
| 977 | return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); | ||
| 978 | } | ||
| 979 | |||
| 980 | // Unconditional branch (immediate) | ||
| 981 | |||
| 982 | pub fn b(offset: i28) Instruction { | ||
| 983 | return unconditionalBranchImmediate(0, offset); | ||
| 984 | } | ||
| 985 | |||
| 986 | pub fn bl(offset: i28) Instruction { | ||
| 987 | return unconditionalBranchImmediate(1, offset); | ||
| 988 | } | ||
| 989 | |||
| 990 | // Nop | ||
| 991 | |||
| 992 | pub fn nop() Instruction { | ||
| 993 | return Instruction{ .no_operation = .{} }; | ||
| 994 | } | ||
| 995 | |||
| 996 | // Logical (shifted register) | ||
| 997 | |||
| 998 | pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 999 | return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1003 | return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1007 | return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1011 | return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1015 | return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1019 | return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1023 | return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1027 | return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | // Add/subtract (immediate) | ||
| 1031 | |||
| 1032 | pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1033 | return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1037 | return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1041 | return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | // Conditional branch | ||
| 1049 | |||
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { | ||
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | // Compare and branch | ||
| 1055 | |||
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { | ||
| 1057 | return compareAndBranch(0b0, rt, offset); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { | ||
| 1061 | return compareAndBranch(0b1, rt, offset); | ||
| 1062 | } | ||
| 1063 | }; | ||
| 1064 | |||
| 1065 | test { | ||
| 1066 | testing.refAllDecls(@This()); | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | test "serialize instructions" { | ||
| 1070 | const Testcase = struct { | ||
| 1071 | inst: Instruction, | ||
| 1072 | expected: u32, | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | const testcases = [_]Testcase{ | ||
| 1076 | .{ // orr x0, xzr, x1 | ||
| 1077 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), | ||
| 1078 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | ||
| 1079 | }, | ||
| 1080 | .{ // orn x0, xzr, x1 | ||
| 1081 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), | ||
| 1082 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | ||
| 1083 | }, | ||
| 1084 | .{ // movz x1, #4 | ||
| 1085 | .inst = Instruction.movz(.x1, 4, 0), | ||
| 1086 | .expected = 0b1_10_100101_00_0000000000000100_00001, | ||
| 1087 | }, | ||
| 1088 | .{ // movz x1, #4, lsl 16 | ||
| 1089 | .inst = Instruction.movz(.x1, 4, 16), | ||
| 1090 | .expected = 0b1_10_100101_01_0000000000000100_00001, | ||
| 1091 | }, | ||
| 1092 | .{ // movz x1, #4, lsl 32 | ||
| 1093 | .inst = Instruction.movz(.x1, 4, 32), | ||
| 1094 | .expected = 0b1_10_100101_10_0000000000000100_00001, | ||
| 1095 | }, | ||
| 1096 | .{ // movz x1, #4, lsl 48 | ||
| 1097 | .inst = Instruction.movz(.x1, 4, 48), | ||
| 1098 | .expected = 0b1_10_100101_11_0000000000000100_00001, | ||
| 1099 | }, | ||
| 1100 | .{ // movz w1, #4 | ||
| 1101 | .inst = Instruction.movz(.w1, 4, 0), | ||
| 1102 | .expected = 0b0_10_100101_00_0000000000000100_00001, | ||
| 1103 | }, | ||
| 1104 | .{ // movz w1, #4, lsl 16 | ||
| 1105 | .inst = Instruction.movz(.w1, 4, 16), | ||
| 1106 | .expected = 0b0_10_100101_01_0000000000000100_00001, | ||
| 1107 | }, | ||
| 1108 | .{ // svc #0 | ||
| 1109 | .inst = Instruction.svc(0), | ||
| 1110 | .expected = 0b1101_0100_000_0000000000000000_00001, | ||
| 1111 | }, | ||
| 1112 | .{ // svc #0x80 ; typical on Darwin | ||
| 1113 | .inst = Instruction.svc(0x80), | ||
| 1114 | .expected = 0b1101_0100_000_0000000010000000_00001, | ||
| 1115 | }, | ||
| 1116 | .{ // ret | ||
| 1117 | .inst = Instruction.ret(null), | ||
| 1118 | .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, | ||
| 1119 | }, | ||
| 1120 | .{ // bl #0x10 | ||
| 1121 | .inst = Instruction.bl(0x10), | ||
| 1122 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, | ||
| 1123 | }, | ||
| 1124 | .{ // ldr x2, [x1] | ||
| 1125 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }), | ||
| 1126 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | ||
| 1127 | }, | ||
| 1128 | .{ // ldr x2, [x1, #1]! | ||
| 1129 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }), | ||
| 1130 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | ||
| 1131 | }, | ||
| 1132 | .{ // ldr x2, [x1], #-1 | ||
| 1133 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }), | ||
| 1134 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | ||
| 1135 | }, | ||
| 1136 | .{ // ldr x2, [x1], (x3) | ||
| 1137 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }), | ||
| 1138 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, | ||
| 1139 | }, | ||
| 1140 | .{ // ldr x2, label | ||
| 1141 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), | ||
| 1142 | .expected = 0b01_011_0_00_0000000000000000001_00010, | ||
| 1143 | }, | ||
| 1144 | .{ // ldrh x7, [x4], #0xaa | ||
| 1145 | .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), | ||
| 1146 | .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, | ||
| 1147 | }, | ||
| 1148 | .{ // ldrb x9, [x15, #0xff]! | ||
| 1149 | .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), | ||
| 1150 | .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, | ||
| 1151 | }, | ||
| 1152 | .{ // str x2, [x1] | ||
| 1153 | .inst = Instruction.str(.x2, .x1, .{}), | ||
| 1154 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | ||
| 1155 | }, | ||
| 1156 | .{ // str x2, [x1], (x3) | ||
| 1157 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), | ||
| 1158 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | ||
| 1159 | }, | ||
| 1160 | .{ // strh w0, [x1] | ||
| 1161 | .inst = Instruction.strh(.w0, .x1, .{}), | ||
| 1162 | .expected = 0b01_111_0_01_00_000000000000_00001_00000, | ||
| 1163 | }, | ||
| 1164 | .{ // strb w8, [x9] | ||
| 1165 | .inst = Instruction.strb(.w8, .x9, .{}), | ||
| 1166 | .expected = 0b00_111_0_01_00_000000000000_01001_01000, | ||
| 1167 | }, | ||
| 1168 | .{ // adr x2, #0x8 | ||
| 1169 | .inst = Instruction.adr(.x2, 0x8), | ||
| 1170 | .expected = 0b0_00_10000_0000000000000000010_00010, | ||
| 1171 | }, | ||
| 1172 | .{ // adr x2, -#0x8 | ||
| 1173 | .inst = Instruction.adr(.x2, -0x8), | ||
| 1174 | .expected = 0b0_00_10000_1111111111111111110_00010, | ||
| 1175 | }, | ||
| 1176 | .{ // adrp x2, #0x8 | ||
| 1177 | .inst = Instruction.adrp(.x2, 0x8), | ||
| 1178 | .expected = 0b1_00_10000_0000000000000000010_00010, | ||
| 1179 | }, | ||
| 1180 | .{ // adrp x2, -#0x8 | ||
| 1181 | .inst = Instruction.adrp(.x2, -0x8), | ||
| 1182 | .expected = 0b1_00_10000_1111111111111111110_00010, | ||
| 1183 | }, | ||
| 1184 | .{ // stp x1, x2, [sp, #8] | ||
| 1185 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | ||
| 1186 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, | ||
| 1187 | }, | ||
| 1188 | .{ // ldp x1, x2, [sp, #8] | ||
| 1189 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | ||
| 1190 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, | ||
| 1191 | }, | ||
| 1192 | .{ // stp x1, x2, [sp, #-16]! | ||
| 1193 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), | ||
| 1194 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, | ||
| 1195 | }, | ||
| 1196 | .{ // ldp x1, x2, [sp], #16 | ||
| 1197 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), | ||
| 1198 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, | ||
| 1199 | }, | ||
| 1200 | .{ // and x0, x4, x2 | ||
| 1201 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), | ||
| 1202 | .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, | ||
| 1203 | }, | ||
| 1204 | .{ // and x0, x4, x2, lsl #0x8 | ||
| 1205 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), | ||
| 1206 | .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, | ||
| 1207 | }, | ||
| 1208 | .{ // add x0, x10, #10 | ||
| 1209 | .inst = Instruction.add(.x0, .x10, 10, false), | ||
| 1210 | .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, | ||
| 1211 | }, | ||
| 1212 | .{ // subs x0, x5, #11, lsl #12 | ||
| 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), | ||
| 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, | ||
| 1215 | }, | ||
| 1216 | .{ // b.hi #-4 | ||
| 1217 | .inst = Instruction.bCond(.hi, -4), | ||
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, | ||
| 1219 | }, | ||
| 1220 | .{ // cbz x10, #40 | ||
| 1221 | .inst = Instruction.cbz(.x10, 40), | ||
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, | ||
| 1223 | }, | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | for (testcases) |case| { | ||
| 1227 | const actual = case.inst.toU32(); | ||
| 1228 | try testing.expectEqual(case.expected, actual); | ||
| 1229 | } | ||
| 1230 | } | ||
src/arch/arm/bits.zig created+1408| ... | @@ -0,0 +1,1408 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | /// The condition field specifies the flags necessary for an | ||
| 6 | /// Instruction to be executed | ||
| 7 | pub const Condition = enum(u4) { | ||
| 8 | /// equal | ||
| 9 | eq, | ||
| 10 | /// not equal | ||
| 11 | ne, | ||
| 12 | /// unsigned higher or same | ||
| 13 | cs, | ||
| 14 | /// unsigned lower | ||
| 15 | cc, | ||
| 16 | /// negative | ||
| 17 | mi, | ||
| 18 | /// positive or zero | ||
| 19 | pl, | ||
| 20 | /// overflow | ||
| 21 | vs, | ||
| 22 | /// no overflow | ||
| 23 | vc, | ||
| 24 | /// unsigned higer | ||
| 25 | hi, | ||
| 26 | /// unsigned lower or same | ||
| 27 | ls, | ||
| 28 | /// greater or equal | ||
| 29 | ge, | ||
| 30 | /// less than | ||
| 31 | lt, | ||
| 32 | /// greater than | ||
| 33 | gt, | ||
| 34 | /// less than or equal | ||
| 35 | le, | ||
| 36 | /// always | ||
| 37 | al, | ||
| 38 | |||
| 39 | /// Converts a std.math.CompareOperator into a condition flag, | ||
| 40 | /// i.e. returns the condition that is true iff the result of the | ||
| 41 | /// comparison is true. Assumes signed comparison | ||
| 42 | pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { | ||
| 43 | return switch (op) { | ||
| 44 | .gte => .ge, | ||
| 45 | .gt => .gt, | ||
| 46 | .neq => .ne, | ||
| 47 | .lt => .lt, | ||
| 48 | .lte => .le, | ||
| 49 | .eq => .eq, | ||
| 50 | }; | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Converts a std.math.CompareOperator into a condition flag, | ||
| 54 | /// i.e. returns the condition that is true iff the result of the | ||
| 55 | /// comparison is true. Assumes unsigned comparison | ||
| 56 | pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { | ||
| 57 | return switch (op) { | ||
| 58 | .gte => .cs, | ||
| 59 | .gt => .hi, | ||
| 60 | .neq => .ne, | ||
| 61 | .lt => .cc, | ||
| 62 | .lte => .ls, | ||
| 63 | .eq => .eq, | ||
| 64 | }; | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Returns the condition which is true iff the given condition is | ||
| 68 | /// false (if such a condition exists) | ||
| 69 | pub fn negate(cond: Condition) Condition { | ||
| 70 | return switch (cond) { | ||
| 71 | .eq => .ne, | ||
| 72 | .ne => .eq, | ||
| 73 | .cs => .cc, | ||
| 74 | .cc => .cs, | ||
| 75 | .mi => .pl, | ||
| 76 | .pl => .mi, | ||
| 77 | .vs => .vc, | ||
| 78 | .vc => .vs, | ||
| 79 | .hi => .ls, | ||
| 80 | .ls => .hi, | ||
| 81 | .ge => .lt, | ||
| 82 | .lt => .ge, | ||
| 83 | .gt => .le, | ||
| 84 | .le => .gt, | ||
| 85 | .al => unreachable, | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | test "condition from CompareOperator" { | ||
| 91 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); | ||
| 92 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); | ||
| 93 | |||
| 94 | try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); | ||
| 95 | try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); | ||
| 96 | |||
| 97 | try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); | ||
| 98 | try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); | ||
| 99 | } | ||
| 100 | |||
| 101 | test "negate condition" { | ||
| 102 | try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); | ||
| 103 | try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Represents a register in the ARM instruction set architecture | ||
| 107 | pub const Register = enum(u5) { | ||
| 108 | r0, | ||
| 109 | r1, | ||
| 110 | r2, | ||
| 111 | r3, | ||
| 112 | r4, | ||
| 113 | r5, | ||
| 114 | r6, | ||
| 115 | r7, | ||
| 116 | r8, | ||
| 117 | r9, | ||
| 118 | r10, | ||
| 119 | r11, | ||
| 120 | r12, | ||
| 121 | r13, | ||
| 122 | r14, | ||
| 123 | r15, | ||
| 124 | |||
| 125 | /// Argument / result / scratch register 1 | ||
| 126 | a1, | ||
| 127 | /// Argument / result / scratch register 2 | ||
| 128 | a2, | ||
| 129 | /// Argument / scratch register 3 | ||
| 130 | a3, | ||
| 131 | /// Argument / scratch register 4 | ||
| 132 | a4, | ||
| 133 | /// Variable-register 1 | ||
| 134 | v1, | ||
| 135 | /// Variable-register 2 | ||
| 136 | v2, | ||
| 137 | /// Variable-register 3 | ||
| 138 | v3, | ||
| 139 | /// Variable-register 4 | ||
| 140 | v4, | ||
| 141 | /// Variable-register 5 | ||
| 142 | v5, | ||
| 143 | /// Platform register | ||
| 144 | v6, | ||
| 145 | /// Variable-register 7 | ||
| 146 | v7, | ||
| 147 | /// Frame pointer or Variable-register 8 | ||
| 148 | fp, | ||
| 149 | /// Intra-Procedure-call scratch register | ||
| 150 | ip, | ||
| 151 | /// Stack pointer | ||
| 152 | sp, | ||
| 153 | /// Link register | ||
| 154 | lr, | ||
| 155 | /// Program counter | ||
| 156 | pc, | ||
| 157 | |||
| 158 | /// Returns the unique 4-bit ID of this register which is used in | ||
| 159 | /// the machine code | ||
| 160 | pub fn id(self: Register) u4 { | ||
| 161 | return @truncate(u4, @enumToInt(self)); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Returns the index into `callee_preserved_regs`. | ||
| 165 | pub fn allocIndex(self: Register) ?u4 { | ||
| 166 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 167 | if (self.id() == cpreg.id()) return i; | ||
| 168 | } | ||
| 169 | return null; | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 173 | return @as(u8, self.id()) + DW.OP.reg0; | ||
| 174 | } | ||
| 175 | }; | ||
| 176 | |||
| 177 | test "Register.id" { | ||
| 178 | try testing.expectEqual(@as(u4, 15), Register.r15.id()); | ||
| 179 | try testing.expectEqual(@as(u4, 15), Register.pc.id()); | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Program status registers containing flags, mode bits and other | ||
| 183 | /// vital information | ||
| 184 | pub const Psr = enum { | ||
| 185 | cpsr, | ||
| 186 | spsr, | ||
| 187 | }; | ||
| 188 | |||
| 189 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | ||
| 190 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 191 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ||
| 192 | |||
| 193 | /// Represents an instruction in the ARM instruction set architecture | ||
| 194 | pub const Instruction = union(enum) { | ||
| 195 | data_processing: packed struct { | ||
| 196 | // Note to self: The order of the fields top-to-bottom is | ||
| 197 | // right-to-left in the actual 32-bit int representation | ||
| 198 | op2: u12, | ||
| 199 | rd: u4, | ||
| 200 | rn: u4, | ||
| 201 | s: u1, | ||
| 202 | opcode: u4, | ||
| 203 | i: u1, | ||
| 204 | fixed: u2 = 0b00, | ||
| 205 | cond: u4, | ||
| 206 | }, | ||
| 207 | multiply: packed struct { | ||
| 208 | rn: u4, | ||
| 209 | fixed_1: u4 = 0b1001, | ||
| 210 | rm: u4, | ||
| 211 | ra: u4, | ||
| 212 | rd: u4, | ||
| 213 | set_cond: u1, | ||
| 214 | accumulate: u1, | ||
| 215 | fixed_2: u6 = 0b000000, | ||
| 216 | cond: u4, | ||
| 217 | }, | ||
| 218 | multiply_long: packed struct { | ||
| 219 | rn: u4, | ||
| 220 | fixed_1: u4 = 0b1001, | ||
| 221 | rm: u4, | ||
| 222 | rdlo: u4, | ||
| 223 | rdhi: u4, | ||
| 224 | set_cond: u1, | ||
| 225 | accumulate: u1, | ||
| 226 | unsigned: u1, | ||
| 227 | fixed_2: u5 = 0b00001, | ||
| 228 | cond: u4, | ||
| 229 | }, | ||
| 230 | integer_saturating_arithmetic: packed struct { | ||
| 231 | rm: u4, | ||
| 232 | fixed_1: u8 = 0b0000_0101, | ||
| 233 | rd: u4, | ||
| 234 | rn: u4, | ||
| 235 | fixed_2: u1 = 0b0, | ||
| 236 | opc: u2, | ||
| 237 | fixed_3: u5 = 0b00010, | ||
| 238 | cond: u4, | ||
| 239 | }, | ||
| 240 | single_data_transfer: packed struct { | ||
| 241 | offset: u12, | ||
| 242 | rd: u4, | ||
| 243 | rn: u4, | ||
| 244 | load_store: u1, | ||
| 245 | write_back: u1, | ||
| 246 | byte_word: u1, | ||
| 247 | up_down: u1, | ||
| 248 | pre_post: u1, | ||
| 249 | imm: u1, | ||
| 250 | fixed: u2 = 0b01, | ||
| 251 | cond: u4, | ||
| 252 | }, | ||
| 253 | extra_load_store: packed struct { | ||
| 254 | imm4l: u4, | ||
| 255 | fixed_1: u1 = 0b1, | ||
| 256 | op2: u2, | ||
| 257 | fixed_2: u1 = 0b1, | ||
| 258 | imm4h: u4, | ||
| 259 | rt: u4, | ||
| 260 | rn: u4, | ||
| 261 | o1: u1, | ||
| 262 | write_back: u1, | ||
| 263 | imm: u1, | ||
| 264 | up_down: u1, | ||
| 265 | pre_index: u1, | ||
| 266 | fixed_3: u3 = 0b000, | ||
| 267 | cond: u4, | ||
| 268 | }, | ||
| 269 | block_data_transfer: packed struct { | ||
| 270 | register_list: u16, | ||
| 271 | rn: u4, | ||
| 272 | load_store: u1, | ||
| 273 | write_back: u1, | ||
| 274 | psr_or_user: u1, | ||
| 275 | up_down: u1, | ||
| 276 | pre_post: u1, | ||
| 277 | fixed: u3 = 0b100, | ||
| 278 | cond: u4, | ||
| 279 | }, | ||
| 280 | branch: packed struct { | ||
| 281 | offset: u24, | ||
| 282 | link: u1, | ||
| 283 | fixed: u3 = 0b101, | ||
| 284 | cond: u4, | ||
| 285 | }, | ||
| 286 | branch_exchange: packed struct { | ||
| 287 | rn: u4, | ||
| 288 | fixed_1: u1 = 0b1, | ||
| 289 | link: u1, | ||
| 290 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | ||
| 291 | cond: u4, | ||
| 292 | }, | ||
| 293 | supervisor_call: packed struct { | ||
| 294 | comment: u24, | ||
| 295 | fixed: u4 = 0b1111, | ||
| 296 | cond: u4, | ||
| 297 | }, | ||
| 298 | breakpoint: packed struct { | ||
| 299 | imm4: u4, | ||
| 300 | fixed_1: u4 = 0b0111, | ||
| 301 | imm12: u12, | ||
| 302 | fixed_2_and_cond: u12 = 0b1110_0001_0010, | ||
| 303 | }, | ||
| 304 | |||
| 305 | /// Represents the possible operations which can be performed by a | ||
| 306 | /// Data Processing instruction | ||
| 307 | const Opcode = enum(u4) { | ||
| 308 | // Rd := Op1 AND Op2 | ||
| 309 | @"and", | ||
| 310 | // Rd := Op1 EOR Op2 | ||
| 311 | eor, | ||
| 312 | // Rd := Op1 - Op2 | ||
| 313 | sub, | ||
| 314 | // Rd := Op2 - Op1 | ||
| 315 | rsb, | ||
| 316 | // Rd := Op1 + Op2 | ||
| 317 | add, | ||
| 318 | // Rd := Op1 + Op2 + C | ||
| 319 | adc, | ||
| 320 | // Rd := Op1 - Op2 + C - 1 | ||
| 321 | sbc, | ||
| 322 | // Rd := Op2 - Op1 + C - 1 | ||
| 323 | rsc, | ||
| 324 | // set condition codes on Op1 AND Op2 | ||
| 325 | tst, | ||
| 326 | // set condition codes on Op1 EOR Op2 | ||
| 327 | teq, | ||
| 328 | // set condition codes on Op1 - Op2 | ||
| 329 | cmp, | ||
| 330 | // set condition codes on Op1 + Op2 | ||
| 331 | cmn, | ||
| 332 | // Rd := Op1 OR Op2 | ||
| 333 | orr, | ||
| 334 | // Rd := Op2 | ||
| 335 | mov, | ||
| 336 | // Rd := Op1 AND NOT Op2 | ||
| 337 | bic, | ||
| 338 | // Rd := NOT Op2 | ||
| 339 | mvn, | ||
| 340 | }; | ||
| 341 | |||
| 342 | /// Represents the second operand to a data processing instruction | ||
| 343 | /// which can either be content from a register or an immediate | ||
| 344 | /// value | ||
| 345 | pub const Operand = union(enum) { | ||
| 346 | Register: packed struct { | ||
| 347 | rm: u4, | ||
| 348 | shift: u8, | ||
| 349 | }, | ||
| 350 | Immediate: packed struct { | ||
| 351 | imm: u8, | ||
| 352 | rotate: u4, | ||
| 353 | }, | ||
| 354 | |||
| 355 | /// Represents multiple ways a register can be shifted. A | ||
| 356 | /// register can be shifted by a specific immediate value or | ||
| 357 | /// by the contents of another register | ||
| 358 | pub const Shift = union(enum) { | ||
| 359 | Immediate: packed struct { | ||
| 360 | fixed: u1 = 0b0, | ||
| 361 | typ: u2, | ||
| 362 | amount: u5, | ||
| 363 | }, | ||
| 364 | Register: packed struct { | ||
| 365 | fixed_1: u1 = 0b1, | ||
| 366 | typ: u2, | ||
| 367 | fixed_2: u1 = 0b0, | ||
| 368 | rs: u4, | ||
| 369 | }, | ||
| 370 | |||
| 371 | pub const Type = enum(u2) { | ||
| 372 | logical_left, | ||
| 373 | logical_right, | ||
| 374 | arithmetic_right, | ||
| 375 | rotate_right, | ||
| 376 | }; | ||
| 377 | |||
| 378 | pub const none = Shift{ | ||
| 379 | .Immediate = .{ | ||
| 380 | .amount = 0, | ||
| 381 | .typ = 0, | ||
| 382 | }, | ||
| 383 | }; | ||
| 384 | |||
| 385 | pub fn toU8(self: Shift) u8 { | ||
| 386 | return switch (self) { | ||
| 387 | .Register => |v| @bitCast(u8, v), | ||
| 388 | .Immediate => |v| @bitCast(u8, v), | ||
| 389 | }; | ||
| 390 | } | ||
| 391 | |||
| 392 | pub fn reg(rs: Register, typ: Type) Shift { | ||
| 393 | return Shift{ | ||
| 394 | .Register = .{ | ||
| 395 | .rs = rs.id(), | ||
| 396 | .typ = @enumToInt(typ), | ||
| 397 | }, | ||
| 398 | }; | ||
| 399 | } | ||
| 400 | |||
| 401 | pub fn imm(amount: u5, typ: Type) Shift { | ||
| 402 | return Shift{ | ||
| 403 | .Immediate = .{ | ||
| 404 | .amount = amount, | ||
| 405 | .typ = @enumToInt(typ), | ||
| 406 | }, | ||
| 407 | }; | ||
| 408 | } | ||
| 409 | }; | ||
| 410 | |||
| 411 | pub fn toU12(self: Operand) u12 { | ||
| 412 | return switch (self) { | ||
| 413 | .Register => |v| @bitCast(u12, v), | ||
| 414 | .Immediate => |v| @bitCast(u12, v), | ||
| 415 | }; | ||
| 416 | } | ||
| 417 | |||
| 418 | pub fn reg(rm: Register, shift: Shift) Operand { | ||
| 419 | return Operand{ | ||
| 420 | .Register = .{ | ||
| 421 | .rm = rm.id(), | ||
| 422 | .shift = shift.toU8(), | ||
| 423 | }, | ||
| 424 | }; | ||
| 425 | } | ||
| 426 | |||
| 427 | pub fn imm(immediate: u8, rotate: u4) Operand { | ||
| 428 | return Operand{ | ||
| 429 | .Immediate = .{ | ||
| 430 | .imm = immediate, | ||
| 431 | .rotate = rotate, | ||
| 432 | }, | ||
| 433 | }; | ||
| 434 | } | ||
| 435 | |||
| 436 | /// Tries to convert an unsigned 32 bit integer into an | ||
| 437 | /// immediate operand using rotation. Returns null when there | ||
| 438 | /// is no conversion | ||
| 439 | pub fn fromU32(x: u32) ?Operand { | ||
| 440 | const masks = comptime blk: { | ||
| 441 | const base_mask: u32 = std.math.maxInt(u8); | ||
| 442 | var result = [_]u32{0} ** 16; | ||
| 443 | for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i); | ||
| 444 | break :blk result; | ||
| 445 | }; | ||
| 446 | |||
| 447 | return for (masks) |mask, i| { | ||
| 448 | if (x & mask == x) { | ||
| 449 | break Operand{ | ||
| 450 | .Immediate = .{ | ||
| 451 | .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)), | ||
| 452 | .rotate = @intCast(u4, i), | ||
| 453 | }, | ||
| 454 | }; | ||
| 455 | } | ||
| 456 | } else null; | ||
| 457 | } | ||
| 458 | }; | ||
| 459 | |||
| 460 | /// Represents the offset operand of a load or store | ||
| 461 | /// instruction. Data can be loaded from memory with either an | ||
| 462 | /// immediate offset or an offset that is stored in some register. | ||
| 463 | pub const Offset = union(enum) { | ||
| 464 | Immediate: u12, | ||
| 465 | Register: packed struct { | ||
| 466 | rm: u4, | ||
| 467 | shift: u8, | ||
| 468 | }, | ||
| 469 | |||
| 470 | pub const none = Offset{ | ||
| 471 | .Immediate = 0, | ||
| 472 | }; | ||
| 473 | |||
| 474 | pub fn toU12(self: Offset) u12 { | ||
| 475 | return switch (self) { | ||
| 476 | .Register => |v| @bitCast(u12, v), | ||
| 477 | .Immediate => |v| v, | ||
| 478 | }; | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn reg(rm: Register, shift: u8) Offset { | ||
| 482 | return Offset{ | ||
| 483 | .Register = .{ | ||
| 484 | .rm = rm.id(), | ||
| 485 | .shift = shift, | ||
| 486 | }, | ||
| 487 | }; | ||
| 488 | } | ||
| 489 | |||
| 490 | pub fn imm(immediate: u12) Offset { | ||
| 491 | return Offset{ | ||
| 492 | .Immediate = immediate, | ||
| 493 | }; | ||
| 494 | } | ||
| 495 | }; | ||
| 496 | |||
| 497 | /// Represents the offset operand of an extra load or store | ||
| 498 | /// instruction. | ||
| 499 | pub const ExtraLoadStoreOffset = union(enum) { | ||
| 500 | immediate: u8, | ||
| 501 | register: u4, | ||
| 502 | |||
| 503 | pub const none = ExtraLoadStoreOffset{ | ||
| 504 | .immediate = 0, | ||
| 505 | }; | ||
| 506 | |||
| 507 | pub fn reg(register: Register) ExtraLoadStoreOffset { | ||
| 508 | return ExtraLoadStoreOffset{ | ||
| 509 | .register = register.id(), | ||
| 510 | }; | ||
| 511 | } | ||
| 512 | |||
| 513 | pub fn imm(immediate: u8) ExtraLoadStoreOffset { | ||
| 514 | return ExtraLoadStoreOffset{ | ||
| 515 | .immediate = immediate, | ||
| 516 | }; | ||
| 517 | } | ||
| 518 | }; | ||
| 519 | |||
| 520 | /// Represents the register list operand to a block data transfer | ||
| 521 | /// instruction | ||
| 522 | pub const RegisterList = packed struct { | ||
| 523 | r0: bool = false, | ||
| 524 | r1: bool = false, | ||
| 525 | r2: bool = false, | ||
| 526 | r3: bool = false, | ||
| 527 | r4: bool = false, | ||
| 528 | r5: bool = false, | ||
| 529 | r6: bool = false, | ||
| 530 | r7: bool = false, | ||
| 531 | r8: bool = false, | ||
| 532 | r9: bool = false, | ||
| 533 | r10: bool = false, | ||
| 534 | r11: bool = false, | ||
| 535 | r12: bool = false, | ||
| 536 | r13: bool = false, | ||
| 537 | r14: bool = false, | ||
| 538 | r15: bool = false, | ||
| 539 | }; | ||
| 540 | |||
| 541 | pub fn toU32(self: Instruction) u32 { | ||
| 542 | return switch (self) { | ||
| 543 | .data_processing => |v| @bitCast(u32, v), | ||
| 544 | .multiply => |v| @bitCast(u32, v), | ||
| 545 | .multiply_long => |v| @bitCast(u32, v), | ||
| 546 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), | ||
| 547 | .single_data_transfer => |v| @bitCast(u32, v), | ||
| 548 | .extra_load_store => |v| @bitCast(u32, v), | ||
| 549 | .block_data_transfer => |v| @bitCast(u32, v), | ||
| 550 | .branch => |v| @bitCast(u32, v), | ||
| 551 | .branch_exchange => |v| @bitCast(u32, v), | ||
| 552 | .supervisor_call => |v| @bitCast(u32, v), | ||
| 553 | .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), | ||
| 554 | }; | ||
| 555 | } | ||
| 556 | |||
| 557 | // Helper functions for the "real" functions below | ||
| 558 | |||
| 559 | fn dataProcessing( | ||
| 560 | cond: Condition, | ||
| 561 | opcode: Opcode, | ||
| 562 | s: u1, | ||
| 563 | rd: Register, | ||
| 564 | rn: Register, | ||
| 565 | op2: Operand, | ||
| 566 | ) Instruction { | ||
| 567 | return Instruction{ | ||
| 568 | .data_processing = .{ | ||
| 569 | .cond = @enumToInt(cond), | ||
| 570 | .i = @boolToInt(op2 == .Immediate), | ||
| 571 | .opcode = @enumToInt(opcode), | ||
| 572 | .s = s, | ||
| 573 | .rn = rn.id(), | ||
| 574 | .rd = rd.id(), | ||
| 575 | .op2 = op2.toU12(), | ||
| 576 | }, | ||
| 577 | }; | ||
| 578 | } | ||
| 579 | |||
| 580 | fn specialMov( | ||
| 581 | cond: Condition, | ||
| 582 | rd: Register, | ||
| 583 | imm: u16, | ||
| 584 | top: bool, | ||
| 585 | ) Instruction { | ||
| 586 | return Instruction{ | ||
| 587 | .data_processing = .{ | ||
| 588 | .cond = @enumToInt(cond), | ||
| 589 | .i = 1, | ||
| 590 | .opcode = if (top) 0b1010 else 0b1000, | ||
| 591 | .s = 0, | ||
| 592 | .rn = @truncate(u4, imm >> 12), | ||
| 593 | .rd = rd.id(), | ||
| 594 | .op2 = @truncate(u12, imm), | ||
| 595 | }, | ||
| 596 | }; | ||
| 597 | } | ||
| 598 | |||
| 599 | fn multiply( | ||
| 600 | cond: Condition, | ||
| 601 | set_cond: u1, | ||
| 602 | rd: Register, | ||
| 603 | rn: Register, | ||
| 604 | rm: Register, | ||
| 605 | ra: ?Register, | ||
| 606 | ) Instruction { | ||
| 607 | return Instruction{ | ||
| 608 | .multiply = .{ | ||
| 609 | .cond = @enumToInt(cond), | ||
| 610 | .accumulate = @boolToInt(ra != null), | ||
| 611 | .set_cond = set_cond, | ||
| 612 | .rd = rd.id(), | ||
| 613 | .rn = rn.id(), | ||
| 614 | .ra = if (ra) |reg| reg.id() else 0b0000, | ||
| 615 | .rm = rm.id(), | ||
| 616 | }, | ||
| 617 | }; | ||
| 618 | } | ||
| 619 | |||
| 620 | fn multiplyLong( | ||
| 621 | cond: Condition, | ||
| 622 | signed: u1, | ||
| 623 | accumulate: u1, | ||
| 624 | set_cond: u1, | ||
| 625 | rdhi: Register, | ||
| 626 | rdlo: Register, | ||
| 627 | rm: Register, | ||
| 628 | rn: Register, | ||
| 629 | ) Instruction { | ||
| 630 | return Instruction{ | ||
| 631 | .multiply_long = .{ | ||
| 632 | .cond = @enumToInt(cond), | ||
| 633 | .unsigned = signed, | ||
| 634 | .accumulate = accumulate, | ||
| 635 | .set_cond = set_cond, | ||
| 636 | .rdlo = rdlo.id(), | ||
| 637 | .rdhi = rdhi.id(), | ||
| 638 | .rn = rn.id(), | ||
| 639 | .rm = rm.id(), | ||
| 640 | }, | ||
| 641 | }; | ||
| 642 | } | ||
| 643 | |||
| 644 | fn integerSaturationArithmetic( | ||
| 645 | cond: Condition, | ||
| 646 | rd: Register, | ||
| 647 | rm: Register, | ||
| 648 | rn: Register, | ||
| 649 | opc: u2, | ||
| 650 | ) Instruction { | ||
| 651 | return Instruction{ | ||
| 652 | .integer_saturating_arithmetic = .{ | ||
| 653 | .rm = rm.id(), | ||
| 654 | .rd = rd.id(), | ||
| 655 | .rn = rn.id(), | ||
| 656 | .opc = opc, | ||
| 657 | .cond = @enumToInt(cond), | ||
| 658 | }, | ||
| 659 | }; | ||
| 660 | } | ||
| 661 | |||
| 662 | fn singleDataTransfer( | ||
| 663 | cond: Condition, | ||
| 664 | rd: Register, | ||
| 665 | rn: Register, | ||
| 666 | offset: Offset, | ||
| 667 | pre_index: bool, | ||
| 668 | positive: bool, | ||
| 669 | byte_word: u1, | ||
| 670 | write_back: bool, | ||
| 671 | load_store: u1, | ||
| 672 | ) Instruction { | ||
| 673 | return Instruction{ | ||
| 674 | .single_data_transfer = .{ | ||
| 675 | .cond = @enumToInt(cond), | ||
| 676 | .rn = rn.id(), | ||
| 677 | .rd = rd.id(), | ||
| 678 | .offset = offset.toU12(), | ||
| 679 | .load_store = load_store, | ||
| 680 | .write_back = @boolToInt(write_back), | ||
| 681 | .byte_word = byte_word, | ||
| 682 | .up_down = @boolToInt(positive), | ||
| 683 | .pre_post = @boolToInt(pre_index), | ||
| 684 | .imm = @boolToInt(offset != .Immediate), | ||
| 685 | }, | ||
| 686 | }; | ||
| 687 | } | ||
| 688 | |||
| 689 | fn extraLoadStore( | ||
| 690 | cond: Condition, | ||
| 691 | pre_index: bool, | ||
| 692 | positive: bool, | ||
| 693 | write_back: bool, | ||
| 694 | o1: u1, | ||
| 695 | op2: u2, | ||
| 696 | rn: Register, | ||
| 697 | rt: Register, | ||
| 698 | offset: ExtraLoadStoreOffset, | ||
| 699 | ) Instruction { | ||
| 700 | const imm4l: u4 = switch (offset) { | ||
| 701 | .immediate => |imm| @truncate(u4, imm), | ||
| 702 | .register => |reg| reg, | ||
| 703 | }; | ||
| 704 | const imm4h: u4 = switch (offset) { | ||
| 705 | .immediate => |imm| @truncate(u4, imm >> 4), | ||
| 706 | .register => 0b0000, | ||
| 707 | }; | ||
| 708 | |||
| 709 | return Instruction{ | ||
| 710 | .extra_load_store = .{ | ||
| 711 | .imm4l = imm4l, | ||
| 712 | .op2 = op2, | ||
| 713 | .imm4h = imm4h, | ||
| 714 | .rt = rt.id(), | ||
| 715 | .rn = rn.id(), | ||
| 716 | .o1 = o1, | ||
| 717 | .write_back = @boolToInt(write_back), | ||
| 718 | .imm = @boolToInt(offset == .immediate), | ||
| 719 | .up_down = @boolToInt(positive), | ||
| 720 | .pre_index = @boolToInt(pre_index), | ||
| 721 | .cond = @enumToInt(cond), | ||
| 722 | }, | ||
| 723 | }; | ||
| 724 | } | ||
| 725 | |||
| 726 | fn blockDataTransfer( | ||
| 727 | cond: Condition, | ||
| 728 | rn: Register, | ||
| 729 | reg_list: RegisterList, | ||
| 730 | pre_post: u1, | ||
| 731 | up_down: u1, | ||
| 732 | psr_or_user: u1, | ||
| 733 | write_back: bool, | ||
| 734 | load_store: u1, | ||
| 735 | ) Instruction { | ||
| 736 | return Instruction{ | ||
| 737 | .block_data_transfer = .{ | ||
| 738 | .register_list = @bitCast(u16, reg_list), | ||
| 739 | .rn = rn.id(), | ||
| 740 | .load_store = load_store, | ||
| 741 | .write_back = @boolToInt(write_back), | ||
| 742 | .psr_or_user = psr_or_user, | ||
| 743 | .up_down = up_down, | ||
| 744 | .pre_post = pre_post, | ||
| 745 | .cond = @enumToInt(cond), | ||
| 746 | }, | ||
| 747 | }; | ||
| 748 | } | ||
| 749 | |||
| 750 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | ||
| 751 | return Instruction{ | ||
| 752 | .branch = .{ | ||
| 753 | .cond = @enumToInt(cond), | ||
| 754 | .link = link, | ||
| 755 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), | ||
| 756 | }, | ||
| 757 | }; | ||
| 758 | } | ||
| 759 | |||
| 760 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | ||
| 761 | return Instruction{ | ||
| 762 | .branch_exchange = .{ | ||
| 763 | .cond = @enumToInt(cond), | ||
| 764 | .link = link, | ||
| 765 | .rn = rn.id(), | ||
| 766 | }, | ||
| 767 | }; | ||
| 768 | } | ||
| 769 | |||
| 770 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | ||
| 771 | return Instruction{ | ||
| 772 | .supervisor_call = .{ | ||
| 773 | .cond = @enumToInt(cond), | ||
| 774 | .comment = comment, | ||
| 775 | }, | ||
| 776 | }; | ||
| 777 | } | ||
| 778 | |||
| 779 | fn breakpoint(imm: u16) Instruction { | ||
| 780 | return Instruction{ | ||
| 781 | .breakpoint = .{ | ||
| 782 | .imm12 = @truncate(u12, imm >> 4), | ||
| 783 | .imm4 = @truncate(u4, imm), | ||
| 784 | }, | ||
| 785 | }; | ||
| 786 | } | ||
| 787 | |||
| 788 | // Public functions replicating assembler syntax as closely as | ||
| 789 | // possible | ||
| 790 | |||
| 791 | // Data processing | ||
| 792 | |||
| 793 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 794 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); | ||
| 795 | } | ||
| 796 | |||
| 797 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 798 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); | ||
| 799 | } | ||
| 800 | |||
| 801 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 802 | return dataProcessing(cond, .eor, 0, rd, rn, op2); | ||
| 803 | } | ||
| 804 | |||
| 805 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 806 | return dataProcessing(cond, .eor, 1, rd, rn, op2); | ||
| 807 | } | ||
| 808 | |||
| 809 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 810 | return dataProcessing(cond, .sub, 0, rd, rn, op2); | ||
| 811 | } | ||
| 812 | |||
| 813 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 814 | return dataProcessing(cond, .sub, 1, rd, rn, op2); | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 818 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); | ||
| 819 | } | ||
| 820 | |||
| 821 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 822 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 826 | return dataProcessing(cond, .add, 0, rd, rn, op2); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 830 | return dataProcessing(cond, .add, 1, rd, rn, op2); | ||
| 831 | } | ||
| 832 | |||
| 833 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 834 | return dataProcessing(cond, .adc, 0, rd, rn, op2); | ||
| 835 | } | ||
| 836 | |||
| 837 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 838 | return dataProcessing(cond, .adc, 1, rd, rn, op2); | ||
| 839 | } | ||
| 840 | |||
| 841 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 842 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); | ||
| 843 | } | ||
| 844 | |||
| 845 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 846 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); | ||
| 847 | } | ||
| 848 | |||
| 849 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 850 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); | ||
| 851 | } | ||
| 852 | |||
| 853 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 854 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); | ||
| 855 | } | ||
| 856 | |||
| 857 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 858 | return dataProcessing(cond, .tst, 1, .r0, rn, op2); | ||
| 859 | } | ||
| 860 | |||
| 861 | pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 862 | return dataProcessing(cond, .teq, 1, .r0, rn, op2); | ||
| 863 | } | ||
| 864 | |||
| 865 | pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 866 | return dataProcessing(cond, .cmp, 1, .r0, rn, op2); | ||
| 867 | } | ||
| 868 | |||
| 869 | pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 870 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | ||
| 871 | } | ||
| 872 | |||
| 873 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 874 | return dataProcessing(cond, .orr, 0, rd, rn, op2); | ||
| 875 | } | ||
| 876 | |||
| 877 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 878 | return dataProcessing(cond, .orr, 1, rd, rn, op2); | ||
| 879 | } | ||
| 880 | |||
| 881 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 882 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 886 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); | ||
| 887 | } | ||
| 888 | |||
| 889 | pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 890 | return dataProcessing(cond, .bic, 0, rd, rn, op2); | ||
| 891 | } | ||
| 892 | |||
| 893 | pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 894 | return dataProcessing(cond, .bic, 1, rd, rn, op2); | ||
| 895 | } | ||
| 896 | |||
| 897 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 898 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); | ||
| 899 | } | ||
| 900 | |||
| 901 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 902 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | ||
| 903 | } | ||
| 904 | |||
| 905 | // Integer Saturating Arithmetic | ||
| 906 | |||
| 907 | pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 908 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); | ||
| 909 | } | ||
| 910 | |||
| 911 | pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 912 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); | ||
| 913 | } | ||
| 914 | |||
| 915 | pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 916 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); | ||
| 917 | } | ||
| 918 | |||
| 919 | pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 920 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); | ||
| 921 | } | ||
| 922 | |||
| 923 | // movw and movt | ||
| 924 | |||
| 925 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { | ||
| 926 | return specialMov(cond, rd, imm, false); | ||
| 927 | } | ||
| 928 | |||
| 929 | pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction { | ||
| 930 | return specialMov(cond, rd, imm, true); | ||
| 931 | } | ||
| 932 | |||
| 933 | // PSR transfer | ||
| 934 | |||
| 935 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | ||
| 936 | return Instruction{ | ||
| 937 | .data_processing = .{ | ||
| 938 | .cond = @enumToInt(cond), | ||
| 939 | .i = 0, | ||
| 940 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, | ||
| 941 | .s = 0, | ||
| 942 | .rn = 0b1111, | ||
| 943 | .rd = rd.id(), | ||
| 944 | .op2 = 0b0000_0000_0000, | ||
| 945 | }, | ||
| 946 | }; | ||
| 947 | } | ||
| 948 | |||
| 949 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { | ||
| 950 | return Instruction{ | ||
| 951 | .data_processing = .{ | ||
| 952 | .cond = @enumToInt(cond), | ||
| 953 | .i = 0, | ||
| 954 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, | ||
| 955 | .s = 0, | ||
| 956 | .rn = 0b1111, | ||
| 957 | .rd = 0b1111, | ||
| 958 | .op2 = op.toU12(), | ||
| 959 | }, | ||
| 960 | }; | ||
| 961 | } | ||
| 962 | |||
| 963 | // Multiply | ||
| 964 | |||
| 965 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | ||
| 966 | return multiply(cond, 0, rd, rn, rm, null); | ||
| 967 | } | ||
| 968 | |||
| 969 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | ||
| 970 | return multiply(cond, 1, rd, rn, rm, null); | ||
| 971 | } | ||
| 972 | |||
| 973 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | ||
| 974 | return multiply(cond, 0, rd, rn, rm, ra); | ||
| 975 | } | ||
| 976 | |||
| 977 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | ||
| 978 | return multiply(cond, 1, rd, rn, rm, ra); | ||
| 979 | } | ||
| 980 | |||
| 981 | // Multiply long | ||
| 982 | |||
| 983 | pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 984 | return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn); | ||
| 985 | } | ||
| 986 | |||
| 987 | pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 988 | return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn); | ||
| 989 | } | ||
| 990 | |||
| 991 | pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 992 | return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn); | ||
| 993 | } | ||
| 994 | |||
| 995 | pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 996 | return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn); | ||
| 997 | } | ||
| 998 | |||
| 999 | pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1000 | return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1004 | return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1008 | return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1012 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | // Single data transfer | ||
| 1016 | |||
| 1017 | pub const OffsetArgs = struct { | ||
| 1018 | pre_index: bool = true, | ||
| 1019 | positive: bool = true, | ||
| 1020 | offset: Offset, | ||
| 1021 | write_back: bool = false, | ||
| 1022 | }; | ||
| 1023 | |||
| 1024 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1025 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1029 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1033 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1037 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | // Extra load/store | ||
| 1041 | |||
| 1042 | pub const ExtraLoadStoreOffsetArgs = struct { | ||
| 1043 | pre_index: bool = true, | ||
| 1044 | positive: bool = true, | ||
| 1045 | offset: ExtraLoadStoreOffset, | ||
| 1046 | write_back: bool = false, | ||
| 1047 | }; | ||
| 1048 | |||
| 1049 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | ||
| 1050 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | ||
| 1054 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | // Block data transfer | ||
| 1058 | |||
| 1059 | pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1060 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1064 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1068 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1072 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | pub const ldmfa = ldmda; | ||
| 1076 | pub const ldmea = ldmdb; | ||
| 1077 | pub const ldmed = ldmib; | ||
| 1078 | pub const ldmfd = ldmia; | ||
| 1079 | pub const ldm = ldmia; | ||
| 1080 | |||
| 1081 | pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1082 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1086 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1090 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1094 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | pub const stmed = stmda; | ||
| 1098 | pub const stmfd = stmdb; | ||
| 1099 | pub const stmfa = stmib; | ||
| 1100 | pub const stmea = stmia; | ||
| 1101 | pub const stm = stmia; | ||
| 1102 | |||
| 1103 | // Branch | ||
| 1104 | |||
| 1105 | pub fn b(cond: Condition, offset: i26) Instruction { | ||
| 1106 | return branch(cond, offset, 0); | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | pub fn bl(cond: Condition, offset: i26) Instruction { | ||
| 1110 | return branch(cond, offset, 1); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | // Branch and exchange | ||
| 1114 | |||
| 1115 | pub fn bx(cond: Condition, rn: Register) Instruction { | ||
| 1116 | return branchExchange(cond, rn, 0); | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | pub fn blx(cond: Condition, rn: Register) Instruction { | ||
| 1120 | return branchExchange(cond, rn, 1); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | // Supervisor Call | ||
| 1124 | |||
| 1125 | pub const swi = svc; | ||
| 1126 | |||
| 1127 | pub fn svc(cond: Condition, comment: u24) Instruction { | ||
| 1128 | return supervisorCall(cond, comment); | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | // Breakpoint | ||
| 1132 | |||
| 1133 | pub fn bkpt(imm: u16) Instruction { | ||
| 1134 | return breakpoint(imm); | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | // Aliases | ||
| 1138 | |||
| 1139 | pub fn nop() Instruction { | ||
| 1140 | return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | pub fn pop(cond: Condition, args: anytype) Instruction { | ||
| 1144 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 1145 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | if (args.len < 1) { | ||
| 1149 | @compileError("Expected at least one register"); | ||
| 1150 | } else if (args.len == 1) { | ||
| 1151 | const reg = args[0]; | ||
| 1152 | return ldr(cond, reg, .sp, .{ | ||
| 1153 | .pre_index = false, | ||
| 1154 | .positive = true, | ||
| 1155 | .offset = Offset.imm(4), | ||
| 1156 | .write_back = false, | ||
| 1157 | }); | ||
| 1158 | } else { | ||
| 1159 | var register_list: u16 = 0; | ||
| 1160 | inline for (args) |arg| { | ||
| 1161 | const reg = @as(Register, arg); | ||
| 1162 | register_list |= @as(u16, 1) << reg.id(); | ||
| 1163 | } | ||
| 1164 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 1165 | } | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | pub fn push(cond: Condition, args: anytype) Instruction { | ||
| 1169 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 1170 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | if (args.len < 1) { | ||
| 1174 | @compileError("Expected at least one register"); | ||
| 1175 | } else if (args.len == 1) { | ||
| 1176 | const reg = args[0]; | ||
| 1177 | return str(cond, reg, .sp, .{ | ||
| 1178 | .pre_index = true, | ||
| 1179 | .positive = false, | ||
| 1180 | .offset = Offset.imm(4), | ||
| 1181 | .write_back = true, | ||
| 1182 | }); | ||
| 1183 | } else { | ||
| 1184 | var register_list: u16 = 0; | ||
| 1185 | inline for (args) |arg| { | ||
| 1186 | const reg = @as(Register, arg); | ||
| 1187 | register_list |= @as(u16, 1) << reg.id(); | ||
| 1188 | } | ||
| 1189 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 1190 | } | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | pub const ShiftAmount = union(enum) { | ||
| 1194 | immediate: u5, | ||
| 1195 | register: Register, | ||
| 1196 | |||
| 1197 | pub fn imm(immediate: u5) ShiftAmount { | ||
| 1198 | return .{ | ||
| 1199 | .immediate = immediate, | ||
| 1200 | }; | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | pub fn reg(register: Register) ShiftAmount { | ||
| 1204 | return .{ | ||
| 1205 | .register = register, | ||
| 1206 | }; | ||
| 1207 | } | ||
| 1208 | }; | ||
| 1209 | |||
| 1210 | pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1211 | return switch (shift) { | ||
| 1212 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | ||
| 1213 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | ||
| 1214 | }; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1218 | return switch (shift) { | ||
| 1219 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | ||
| 1220 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | ||
| 1221 | }; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1225 | return switch (shift) { | ||
| 1226 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | ||
| 1227 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | ||
| 1228 | }; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1232 | return switch (shift) { | ||
| 1233 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | ||
| 1234 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | ||
| 1235 | }; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1239 | return switch (shift) { | ||
| 1240 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | ||
| 1241 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | ||
| 1242 | }; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1246 | return switch (shift) { | ||
| 1247 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | ||
| 1248 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | ||
| 1249 | }; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1253 | return switch (shift) { | ||
| 1254 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | ||
| 1255 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | ||
| 1256 | }; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1260 | return switch (shift) { | ||
| 1261 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | ||
| 1262 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | ||
| 1263 | }; | ||
| 1264 | } | ||
| 1265 | }; | ||
| 1266 | |||
| 1267 | test "serialize instructions" { | ||
| 1268 | const Testcase = struct { | ||
| 1269 | inst: Instruction, | ||
| 1270 | expected: u32, | ||
| 1271 | }; | ||
| 1272 | |||
| 1273 | const testcases = [_]Testcase{ | ||
| 1274 | .{ // add r0, r0, r0 | ||
| 1275 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | ||
| 1276 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | ||
| 1277 | }, | ||
| 1278 | .{ // mov r4, r2 | ||
| 1279 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | ||
| 1280 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | ||
| 1281 | }, | ||
| 1282 | .{ // mov r0, #42 | ||
| 1283 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), | ||
| 1284 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | ||
| 1285 | }, | ||
| 1286 | .{ // mrs r5, cpsr | ||
| 1287 | .inst = Instruction.mrs(.al, .r5, .cpsr), | ||
| 1288 | .expected = 0b1110_00010_0_001111_0101_000000000000, | ||
| 1289 | }, | ||
| 1290 | .{ // mul r0, r1, r2 | ||
| 1291 | .inst = Instruction.mul(.al, .r0, .r1, .r2), | ||
| 1292 | .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001, | ||
| 1293 | }, | ||
| 1294 | .{ // umlal r0, r1, r5, r6 | ||
| 1295 | .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6), | ||
| 1296 | .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101, | ||
| 1297 | }, | ||
| 1298 | .{ // ldr r0, [r2, #42] | ||
| 1299 | .inst = Instruction.ldr(.al, .r0, .r2, .{ | ||
| 1300 | .offset = Instruction.Offset.imm(42), | ||
| 1301 | }), | ||
| 1302 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | ||
| 1303 | }, | ||
| 1304 | .{ // str r0, [r3] | ||
| 1305 | .inst = Instruction.str(.al, .r0, .r3, .{ | ||
| 1306 | .offset = Instruction.Offset.none, | ||
| 1307 | }), | ||
| 1308 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | ||
| 1309 | }, | ||
| 1310 | .{ // strh r1, [r5] | ||
| 1311 | .inst = Instruction.strh(.al, .r1, .r5, .{ | ||
| 1312 | .offset = Instruction.ExtraLoadStoreOffset.none, | ||
| 1313 | }), | ||
| 1314 | .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000, | ||
| 1315 | }, | ||
| 1316 | .{ // b #12 | ||
| 1317 | .inst = Instruction.b(.al, 12), | ||
| 1318 | .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011, | ||
| 1319 | }, | ||
| 1320 | .{ // bl #-4 | ||
| 1321 | .inst = Instruction.bl(.al, -4), | ||
| 1322 | .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111, | ||
| 1323 | }, | ||
| 1324 | .{ // bx lr | ||
| 1325 | .inst = Instruction.bx(.al, .lr), | ||
| 1326 | .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, | ||
| 1327 | }, | ||
| 1328 | .{ // svc #0 | ||
| 1329 | .inst = Instruction.svc(.al, 0), | ||
| 1330 | .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, | ||
| 1331 | }, | ||
| 1332 | .{ // bkpt #42 | ||
| 1333 | .inst = Instruction.bkpt(42), | ||
| 1334 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | ||
| 1335 | }, | ||
| 1336 | .{ // stmdb r9, {r0} | ||
| 1337 | .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), | ||
| 1338 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, | ||
| 1339 | }, | ||
| 1340 | .{ // ldmea r4!, {r2, r5} | ||
| 1341 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | ||
| 1342 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | ||
| 1343 | }, | ||
| 1344 | .{ // qadd r0, r7, r8 | ||
| 1345 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), | ||
| 1346 | .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, | ||
| 1347 | }, | ||
| 1348 | }; | ||
| 1349 | |||
| 1350 | for (testcases) |case| { | ||
| 1351 | const actual = case.inst.toU32(); | ||
| 1352 | try testing.expectEqual(case.expected, actual); | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | test "aliases" { | ||
| 1357 | const Testcase = struct { | ||
| 1358 | expected: Instruction, | ||
| 1359 | actual: Instruction, | ||
| 1360 | }; | ||
| 1361 | |||
| 1362 | const testcases = [_]Testcase{ | ||
| 1363 | .{ // pop { r6 } | ||
| 1364 | .actual = Instruction.pop(.al, .{.r6}), | ||
| 1365 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | ||
| 1366 | .pre_index = false, | ||
| 1367 | .positive = true, | ||
| 1368 | .offset = Instruction.Offset.imm(4), | ||
| 1369 | .write_back = false, | ||
| 1370 | }), | ||
| 1371 | }, | ||
| 1372 | .{ // pop { r1, r5 } | ||
| 1373 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), | ||
| 1374 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), | ||
| 1375 | }, | ||
| 1376 | .{ // push { r3 } | ||
| 1377 | .actual = Instruction.push(.al, .{.r3}), | ||
| 1378 | .expected = Instruction.str(.al, .r3, .sp, .{ | ||
| 1379 | .pre_index = true, | ||
| 1380 | .positive = false, | ||
| 1381 | .offset = Instruction.Offset.imm(4), | ||
| 1382 | .write_back = true, | ||
| 1383 | }), | ||
| 1384 | }, | ||
| 1385 | .{ // push { r0, r2 } | ||
| 1386 | .actual = Instruction.push(.al, .{ .r0, .r2 }), | ||
| 1387 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), | ||
| 1388 | }, | ||
| 1389 | .{ // lsl r4, r5, #5 | ||
| 1390 | .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), | ||
| 1391 | .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( | ||
| 1392 | .r5, | ||
| 1393 | Instruction.Operand.Shift.imm(5, .logical_left), | ||
| 1394 | )), | ||
| 1395 | }, | ||
| 1396 | .{ // asrs r1, r1, r3 | ||
| 1397 | .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), | ||
| 1398 | .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( | ||
| 1399 | .r1, | ||
| 1400 | Instruction.Operand.Shift.reg(.r3, .arithmetic_right), | ||
| 1401 | )), | ||
| 1402 | }, | ||
| 1403 | }; | ||
| 1404 | |||
| 1405 | for (testcases) |case| { | ||
| 1406 | try testing.expectEqual(case.expected.toU32(), case.actual.toU32()); | ||
| 1407 | } | ||
| 1408 | } | ||
src/arch/riscv64/bits.zig created+470| ... | @@ -0,0 +1,470 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | // TODO: this is only tagged to facilitate the monstrosity. | ||
| 7 | // Once packed structs work make it packed. | ||
| 8 | pub const Instruction = union(enum) { | ||
| 9 | R: packed struct { | ||
| 10 | opcode: u7, | ||
| 11 | rd: u5, | ||
| 12 | funct3: u3, | ||
| 13 | rs1: u5, | ||
| 14 | rs2: u5, | ||
| 15 | funct7: u7, | ||
| 16 | }, | ||
| 17 | I: packed struct { | ||
| 18 | opcode: u7, | ||
| 19 | rd: u5, | ||
| 20 | funct3: u3, | ||
| 21 | rs1: u5, | ||
| 22 | imm0_11: u12, | ||
| 23 | }, | ||
| 24 | S: packed struct { | ||
| 25 | opcode: u7, | ||
| 26 | imm0_4: u5, | ||
| 27 | funct3: u3, | ||
| 28 | rs1: u5, | ||
| 29 | rs2: u5, | ||
| 30 | imm5_11: u7, | ||
| 31 | }, | ||
| 32 | B: packed struct { | ||
| 33 | opcode: u7, | ||
| 34 | imm11: u1, | ||
| 35 | imm1_4: u4, | ||
| 36 | funct3: u3, | ||
| 37 | rs1: u5, | ||
| 38 | rs2: u5, | ||
| 39 | imm5_10: u6, | ||
| 40 | imm12: u1, | ||
| 41 | }, | ||
| 42 | U: packed struct { | ||
| 43 | opcode: u7, | ||
| 44 | rd: u5, | ||
| 45 | imm12_31: u20, | ||
| 46 | }, | ||
| 47 | J: packed struct { | ||
| 48 | opcode: u7, | ||
| 49 | rd: u5, | ||
| 50 | imm12_19: u8, | ||
| 51 | imm11: u1, | ||
| 52 | imm1_10: u10, | ||
| 53 | imm20: u1, | ||
| 54 | }, | ||
| 55 | |||
| 56 | // TODO: once packed structs work we can remove this monstrosity. | ||
| 57 | pub fn toU32(self: Instruction) u32 { | ||
| 58 | return switch (self) { | ||
| 59 | .R => |v| @bitCast(u32, v), | ||
| 60 | .I => |v| @bitCast(u32, v), | ||
| 61 | .S => |v| @bitCast(u32, v), | ||
| 62 | .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31), | ||
| 63 | .U => |v| @bitCast(u32, v), | ||
| 64 | .J => |v| @bitCast(u32, v), | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | |||
| 68 | fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction { | ||
| 69 | return Instruction{ | ||
| 70 | .R = .{ | ||
| 71 | .opcode = op, | ||
| 72 | .funct3 = fn3, | ||
| 73 | .funct7 = fn7, | ||
| 74 | .rd = @enumToInt(rd), | ||
| 75 | .rs1 = @enumToInt(r1), | ||
| 76 | .rs2 = @enumToInt(r2), | ||
| 77 | }, | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | // RISC-V is all signed all the time -- convert immediates to unsigned for processing | ||
| 82 | fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction { | ||
| 83 | const umm = @bitCast(u12, imm); | ||
| 84 | |||
| 85 | return Instruction{ | ||
| 86 | .I = .{ | ||
| 87 | .opcode = op, | ||
| 88 | .funct3 = fn3, | ||
| 89 | .rd = @enumToInt(rd), | ||
| 90 | .rs1 = @enumToInt(r1), | ||
| 91 | .imm0_11 = umm, | ||
| 92 | }, | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | |||
| 96 | fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction { | ||
| 97 | const umm = @bitCast(u12, imm); | ||
| 98 | |||
| 99 | return Instruction{ | ||
| 100 | .S = .{ | ||
| 101 | .opcode = op, | ||
| 102 | .funct3 = fn3, | ||
| 103 | .rs1 = @enumToInt(r1), | ||
| 104 | .rs2 = @enumToInt(r2), | ||
| 105 | .imm0_4 = @truncate(u5, umm), | ||
| 106 | .imm5_11 = @truncate(u7, umm >> 5), | ||
| 107 | }, | ||
| 108 | }; | ||
| 109 | } | ||
| 110 | |||
| 111 | // Use significance value rather than bit value, same for J-type | ||
| 112 | // -- less burden on callsite, bonus semantic checking | ||
| 113 | fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { | ||
| 114 | const umm = @bitCast(u13, imm); | ||
| 115 | assert(umm % 2 == 0); // misaligned branch target | ||
| 116 | |||
| 117 | return Instruction{ | ||
| 118 | .B = .{ | ||
| 119 | .opcode = op, | ||
| 120 | .funct3 = fn3, | ||
| 121 | .rs1 = @enumToInt(r1), | ||
| 122 | .rs2 = @enumToInt(r2), | ||
| 123 | .imm1_4 = @truncate(u4, umm >> 1), | ||
| 124 | .imm5_10 = @truncate(u6, umm >> 5), | ||
| 125 | .imm11 = @truncate(u1, umm >> 11), | ||
| 126 | .imm12 = @truncate(u1, umm >> 12), | ||
| 127 | }, | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | // We have to extract the 20 bits anyway -- let's not make it more painful | ||
| 132 | fn uType(op: u7, rd: Register, imm: i20) Instruction { | ||
| 133 | const umm = @bitCast(u20, imm); | ||
| 134 | |||
| 135 | return Instruction{ | ||
| 136 | .U = .{ | ||
| 137 | .opcode = op, | ||
| 138 | .rd = @enumToInt(rd), | ||
| 139 | .imm12_31 = umm, | ||
| 140 | }, | ||
| 141 | }; | ||
| 142 | } | ||
| 143 | |||
| 144 | fn jType(op: u7, rd: Register, imm: i21) Instruction { | ||
| 145 | const umm = @bitCast(u21, imm); | ||
| 146 | assert(umm % 2 == 0); // misaligned jump target | ||
| 147 | |||
| 148 | return Instruction{ | ||
| 149 | .J = .{ | ||
| 150 | .opcode = op, | ||
| 151 | .rd = @enumToInt(rd), | ||
| 152 | .imm1_10 = @truncate(u10, umm >> 1), | ||
| 153 | .imm11 = @truncate(u1, umm >> 11), | ||
| 154 | .imm12_19 = @truncate(u8, umm >> 12), | ||
| 155 | .imm20 = @truncate(u1, umm >> 20), | ||
| 156 | }, | ||
| 157 | }; | ||
| 158 | } | ||
| 159 | |||
| 160 | // The meat and potatoes. Arguments are in the order in which they would appear in assembly code. | ||
| 161 | |||
| 162 | // Arithmetic/Logical, Register-Register | ||
| 163 | |||
| 164 | pub fn add(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 165 | return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2); | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn sub(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 169 | return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 173 | return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2); | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 177 | return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2); | ||
| 178 | } | ||
| 179 | |||
| 180 | pub fn xor(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 181 | return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn sll(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 185 | return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2); | ||
| 186 | } | ||
| 187 | |||
| 188 | pub fn srl(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 189 | return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2); | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn sra(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 193 | return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2); | ||
| 194 | } | ||
| 195 | |||
| 196 | pub fn slt(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 197 | return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2); | ||
| 198 | } | ||
| 199 | |||
| 200 | pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 201 | return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2); | ||
| 202 | } | ||
| 203 | |||
| 204 | // Arithmetic/Logical, Register-Register (32-bit) | ||
| 205 | |||
| 206 | pub fn addw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 207 | return rType(0b0111011, 0b000, rd, r1, r2); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn subw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 211 | return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2); | ||
| 212 | } | ||
| 213 | |||
| 214 | pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 215 | return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2); | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 219 | return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2); | ||
| 220 | } | ||
| 221 | |||
| 222 | pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 223 | return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2); | ||
| 224 | } | ||
| 225 | |||
| 226 | // Arithmetic/Logical, Register-Immediate | ||
| 227 | |||
| 228 | pub fn addi(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 229 | return iType(0b0010011, 0b000, rd, r1, imm); | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn andi(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 233 | return iType(0b0010011, 0b111, rd, r1, imm); | ||
| 234 | } | ||
| 235 | |||
| 236 | pub fn ori(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 237 | return iType(0b0010011, 0b110, rd, r1, imm); | ||
| 238 | } | ||
| 239 | |||
| 240 | pub fn xori(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 241 | return iType(0b0010011, 0b100, rd, r1, imm); | ||
| 242 | } | ||
| 243 | |||
| 244 | pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 245 | return iType(0b0010011, 0b001, rd, r1, shamt); | ||
| 246 | } | ||
| 247 | |||
| 248 | pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 249 | return iType(0b0010011, 0b101, rd, r1, shamt); | ||
| 250 | } | ||
| 251 | |||
| 252 | pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 253 | return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt); | ||
| 254 | } | ||
| 255 | |||
| 256 | pub fn slti(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 257 | return iType(0b0010011, 0b010, rd, r1, imm); | ||
| 258 | } | ||
| 259 | |||
| 260 | pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction { | ||
| 261 | return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm)); | ||
| 262 | } | ||
| 263 | |||
| 264 | // Arithmetic/Logical, Register-Immediate (32-bit) | ||
| 265 | |||
| 266 | pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 267 | return iType(0b0011011, 0b000, rd, r1, imm); | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 271 | return iType(0b0011011, 0b001, rd, r1, shamt); | ||
| 272 | } | ||
| 273 | |||
| 274 | pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 275 | return iType(0b0011011, 0b101, rd, r1, shamt); | ||
| 276 | } | ||
| 277 | |||
| 278 | pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 279 | return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt); | ||
| 280 | } | ||
| 281 | |||
| 282 | // Upper Immediate | ||
| 283 | |||
| 284 | pub fn lui(rd: Register, imm: i20) Instruction { | ||
| 285 | return uType(0b0110111, rd, imm); | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn auipc(rd: Register, imm: i20) Instruction { | ||
| 289 | return uType(0b0010111, rd, imm); | ||
| 290 | } | ||
| 291 | |||
| 292 | // Load | ||
| 293 | |||
| 294 | pub fn ld(rd: Register, offset: i12, base: Register) Instruction { | ||
| 295 | return iType(0b0000011, 0b011, rd, base, offset); | ||
| 296 | } | ||
| 297 | |||
| 298 | pub fn lw(rd: Register, offset: i12, base: Register) Instruction { | ||
| 299 | return iType(0b0000011, 0b010, rd, base, offset); | ||
| 300 | } | ||
| 301 | |||
| 302 | pub fn lwu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 303 | return iType(0b0000011, 0b110, rd, base, offset); | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn lh(rd: Register, offset: i12, base: Register) Instruction { | ||
| 307 | return iType(0b0000011, 0b001, rd, base, offset); | ||
| 308 | } | ||
| 309 | |||
| 310 | pub fn lhu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 311 | return iType(0b0000011, 0b101, rd, base, offset); | ||
| 312 | } | ||
| 313 | |||
| 314 | pub fn lb(rd: Register, offset: i12, base: Register) Instruction { | ||
| 315 | return iType(0b0000011, 0b000, rd, base, offset); | ||
| 316 | } | ||
| 317 | |||
| 318 | pub fn lbu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 319 | return iType(0b0000011, 0b100, rd, base, offset); | ||
| 320 | } | ||
| 321 | |||
| 322 | // Store | ||
| 323 | |||
| 324 | pub fn sd(rs: Register, offset: i12, base: Register) Instruction { | ||
| 325 | return sType(0b0100011, 0b011, base, rs, offset); | ||
| 326 | } | ||
| 327 | |||
| 328 | pub fn sw(rs: Register, offset: i12, base: Register) Instruction { | ||
| 329 | return sType(0b0100011, 0b010, base, rs, offset); | ||
| 330 | } | ||
| 331 | |||
| 332 | pub fn sh(rs: Register, offset: i12, base: Register) Instruction { | ||
| 333 | return sType(0b0100011, 0b001, base, rs, offset); | ||
| 334 | } | ||
| 335 | |||
| 336 | pub fn sb(rs: Register, offset: i12, base: Register) Instruction { | ||
| 337 | return sType(0b0100011, 0b000, base, rs, offset); | ||
| 338 | } | ||
| 339 | |||
| 340 | // Fence | ||
| 341 | // TODO: implement fence | ||
| 342 | |||
| 343 | // Branch | ||
| 344 | |||
| 345 | pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 346 | return bType(0b1100011, 0b000, r1, r2, offset); | ||
| 347 | } | ||
| 348 | |||
| 349 | pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 350 | return bType(0b1100011, 0b001, r1, r2, offset); | ||
| 351 | } | ||
| 352 | |||
| 353 | pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 354 | return bType(0b1100011, 0b100, r1, r2, offset); | ||
| 355 | } | ||
| 356 | |||
| 357 | pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 358 | return bType(0b1100011, 0b101, r1, r2, offset); | ||
| 359 | } | ||
| 360 | |||
| 361 | pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 362 | return bType(0b1100011, 0b110, r1, r2, offset); | ||
| 363 | } | ||
| 364 | |||
| 365 | pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 366 | return bType(0b1100011, 0b111, r1, r2, offset); | ||
| 367 | } | ||
| 368 | |||
| 369 | // Jump | ||
| 370 | |||
| 371 | pub fn jal(link: Register, offset: i21) Instruction { | ||
| 372 | return jType(0b1101111, link, offset); | ||
| 373 | } | ||
| 374 | |||
| 375 | pub fn jalr(link: Register, offset: i12, base: Register) Instruction { | ||
| 376 | return iType(0b1100111, 0b000, link, base, offset); | ||
| 377 | } | ||
| 378 | |||
| 379 | // System | ||
| 380 | |||
| 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); | ||
| 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); | ||
| 383 | }; | ||
| 384 | |||
| 385 | // zig fmt: off | ||
| 386 | pub const RawRegister = enum(u5) { | ||
| 387 | x0, x1, x2, x3, x4, x5, x6, x7, | ||
| 388 | x8, x9, x10, x11, x12, x13, x14, x15, | ||
| 389 | x16, x17, x18, x19, x20, x21, x22, x23, | ||
| 390 | x24, x25, x26, x27, x28, x29, x30, x31, | ||
| 391 | |||
| 392 | pub fn dwarfLocOp(reg: RawRegister) u8 { | ||
| 393 | return @enumToInt(reg) + DW.OP.reg0; | ||
| 394 | } | ||
| 395 | }; | ||
| 396 | |||
| 397 | pub const Register = enum(u5) { | ||
| 398 | // 64 bit registers | ||
| 399 | zero, // zero | ||
| 400 | ra, // return address. caller saved | ||
| 401 | sp, // stack pointer. callee saved. | ||
| 402 | gp, // global pointer | ||
| 403 | tp, // thread pointer | ||
| 404 | t0, t1, t2, // temporaries. caller saved. | ||
| 405 | s0, // s0/fp, callee saved. | ||
| 406 | s1, // callee saved. | ||
| 407 | a0, a1, // fn args/return values. caller saved. | ||
| 408 | a2, a3, a4, a5, a6, a7, // fn args. caller saved. | ||
| 409 | s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. | ||
| 410 | t3, t4, t5, t6, // caller saved | ||
| 411 | |||
| 412 | pub fn parseRegName(name: []const u8) ?Register { | ||
| 413 | if(std.meta.stringToEnum(Register, name)) |reg| return reg; | ||
| 414 | if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); | ||
| 415 | return null; | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Returns the index into `callee_preserved_regs`. | ||
| 419 | pub fn allocIndex(self: Register) ?u4 { | ||
| 420 | inline for(callee_preserved_regs) |cpreg, i| { | ||
| 421 | if(self == cpreg) return i; | ||
| 422 | } | ||
| 423 | return null; | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn dwarfLocOp(reg: Register) u8 { | ||
| 427 | return @as(u8, @enumToInt(reg)) + DW.OP.reg0; | ||
| 428 | } | ||
| 429 | }; | ||
| 430 | |||
| 431 | // zig fmt: on | ||
| 432 | |||
| 433 | pub const callee_preserved_regs = [_]Register{ | ||
| 434 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | ||
| 435 | }; | ||
| 436 | |||
| 437 | test "serialize instructions" { | ||
| 438 | const Testcase = struct { | ||
| 439 | inst: Instruction, | ||
| 440 | expected: u32, | ||
| 441 | }; | ||
| 442 | |||
| 443 | const testcases = [_]Testcase{ | ||
| 444 | .{ // add t6, zero, zero | ||
| 445 | .inst = Instruction.add(.t6, .zero, .zero), | ||
| 446 | .expected = 0b0000000_00000_00000_000_11111_0110011, | ||
| 447 | }, | ||
| 448 | .{ // sd s0, 0x7f(s0) | ||
| 449 | .inst = Instruction.sd(.s0, 0x7f, .s0), | ||
| 450 | .expected = 0b0000011_01000_01000_011_11111_0100011, | ||
| 451 | }, | ||
| 452 | .{ // bne s0, s1, 0x42 | ||
| 453 | .inst = Instruction.bne(.s0, .s1, 0x42), | ||
| 454 | .expected = 0b0_000010_01001_01000_001_0001_0_1100011, | ||
| 455 | }, | ||
| 456 | .{ // j 0x1a | ||
| 457 | .inst = Instruction.jal(.zero, 0x1a), | ||
| 458 | .expected = 0b0_0000001101_0_00000000_00000_1101111, | ||
| 459 | }, | ||
| 460 | .{ // ebreak | ||
| 461 | .inst = Instruction.ebreak, | ||
| 462 | .expected = 0b000000000001_00000_000_00000_1110011, | ||
| 463 | }, | ||
| 464 | }; | ||
| 465 | |||
| 466 | for (testcases) |case| { | ||
| 467 | const actual = case.inst.toU32(); | ||
| 468 | try testing.expectEqual(case.expected, actual); | ||
| 469 | } | ||
| 470 | } | ||
src/arch/x86/bits.zig created+123| ... | @@ -0,0 +1,123 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | |||
| 4 | // zig fmt: off | ||
| 5 | pub const Register = enum(u8) { | ||
| 6 | // 0 through 7, 32-bit registers. id is int value | ||
| 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | ||
| 8 | |||
| 9 | // 8-15, 16-bit registers. id is int value - 8. | ||
| 10 | ax, cx, dx, bx, sp, bp, si, di, | ||
| 11 | |||
| 12 | // 16-23, 8-bit registers. id is int value - 16. | ||
| 13 | al, cl, dl, bl, ah, ch, dh, bh, | ||
| 14 | |||
| 15 | /// Returns the bit-width of the register. | ||
| 16 | pub fn size(self: @This()) u7 { | ||
| 17 | return switch (@enumToInt(self)) { | ||
| 18 | 0...7 => 32, | ||
| 19 | 8...15 => 16, | ||
| 20 | 16...23 => 8, | ||
| 21 | else => unreachable, | ||
| 22 | }; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Returns the register's id. This is used in practically every opcode the | ||
| 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move | ||
| 27 | /// instruction, and is used in the R/M byte. | ||
| 28 | pub fn id(self: @This()) u3 { | ||
| 29 | return @truncate(u3, @enumToInt(self)); | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Returns the index into `callee_preserved_regs`. | ||
| 33 | pub fn allocIndex(self: Register) ?u4 { | ||
| 34 | return switch (self) { | ||
| 35 | .eax, .ax, .al => 0, | ||
| 36 | .ecx, .cx, .cl => 1, | ||
| 37 | .edx, .dx, .dl => 2, | ||
| 38 | .esi, .si => 3, | ||
| 39 | .edi, .di => 4, | ||
| 40 | else => null, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Convert from any register to its 32 bit alias. | ||
| 45 | pub fn to32(self: Register) Register { | ||
| 46 | return @intToEnum(Register, @as(u8, self.id())); | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Convert from any register to its 16 bit alias. | ||
| 50 | pub fn to16(self: Register) Register { | ||
| 51 | return @intToEnum(Register, @as(u8, self.id()) + 8); | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Convert from any register to its 8 bit alias. | ||
| 55 | pub fn to8(self: Register) Register { | ||
| 56 | return @intToEnum(Register, @as(u8, self.id()) + 16); | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | pub fn dwarfLocOp(reg: Register) u8 { | ||
| 61 | return switch (reg.to32()) { | ||
| 62 | .eax => DW.OP.reg0, | ||
| 63 | .ecx => DW.OP.reg1, | ||
| 64 | .edx => DW.OP.reg2, | ||
| 65 | .ebx => DW.OP.reg3, | ||
| 66 | .esp => DW.OP.reg4, | ||
| 67 | .ebp => DW.OP.reg5, | ||
| 68 | .esi => DW.OP.reg6, | ||
| 69 | .edi => DW.OP.reg7, | ||
| 70 | else => unreachable, | ||
| 71 | }; | ||
| 72 | } | ||
| 73 | }; | ||
| 74 | |||
| 75 | // zig fmt: on | ||
| 76 | |||
| 77 | pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; | ||
| 78 | |||
| 79 | // TODO add these to Register enum and corresponding dwarfLocOp | ||
| 80 | // // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. | ||
| 81 | // RA = (8, "RA"), | ||
| 82 | // | ||
| 83 | // ST0 = (11, "st0"), | ||
| 84 | // ST1 = (12, "st1"), | ||
| 85 | // ST2 = (13, "st2"), | ||
| 86 | // ST3 = (14, "st3"), | ||
| 87 | // ST4 = (15, "st4"), | ||
| 88 | // ST5 = (16, "st5"), | ||
| 89 | // ST6 = (17, "st6"), | ||
| 90 | // ST7 = (18, "st7"), | ||
| 91 | // | ||
| 92 | // XMM0 = (21, "xmm0"), | ||
| 93 | // XMM1 = (22, "xmm1"), | ||
| 94 | // XMM2 = (23, "xmm2"), | ||
| 95 | // XMM3 = (24, "xmm3"), | ||
| 96 | // XMM4 = (25, "xmm4"), | ||
| 97 | // XMM5 = (26, "xmm5"), | ||
| 98 | // XMM6 = (27, "xmm6"), | ||
| 99 | // XMM7 = (28, "xmm7"), | ||
| 100 | // | ||
| 101 | // MM0 = (29, "mm0"), | ||
| 102 | // MM1 = (30, "mm1"), | ||
| 103 | // MM2 = (31, "mm2"), | ||
| 104 | // MM3 = (32, "mm3"), | ||
| 105 | // MM4 = (33, "mm4"), | ||
| 106 | // MM5 = (34, "mm5"), | ||
| 107 | // MM6 = (35, "mm6"), | ||
| 108 | // MM7 = (36, "mm7"), | ||
| 109 | // | ||
| 110 | // MXCSR = (39, "mxcsr"), | ||
| 111 | // | ||
| 112 | // ES = (40, "es"), | ||
| 113 | // CS = (41, "cs"), | ||
| 114 | // SS = (42, "ss"), | ||
| 115 | // DS = (43, "ds"), | ||
| 116 | // FS = (44, "fs"), | ||
| 117 | // GS = (45, "gs"), | ||
| 118 | // | ||
| 119 | // TR = (48, "tr"), | ||
| 120 | // LDTR = (49, "ldtr"), | ||
| 121 | // | ||
| 122 | // FS_BASE = (93, "fs.base"), | ||
| 123 | // GS_BASE = (94, "gs.base"), | ||
src/arch/x86_64/bits.zig created+716| ... | @@ -0,0 +1,716 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const mem = std.mem; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const ArrayList = std.ArrayList; | ||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | const DW = std.dwarf; | ||
| 8 | |||
| 9 | // zig fmt: off | ||
| 10 | |||
| 11 | /// Definitions of all of the x64 registers. The order is semantically meaningful. | ||
| 12 | /// The registers are defined such that IDs go in descending order of 64-bit, | ||
| 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen | ||
| 14 | /// registers. This results in some useful properties: | ||
| 15 | /// | ||
| 16 | /// Any 64-bit register can be turned into its 32-bit form by adding 16, and | ||
| 17 | /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it | ||
| 18 | /// works for all except for sp, bp, si, and di, which do *not* have an 8-bit | ||
| 19 | /// form. | ||
| 20 | /// | ||
| 21 | /// If (register & 8) is set, the register is extended. | ||
| 22 | /// | ||
| 23 | /// The ID can be easily determined by figuring out what range the register is | ||
| 24 | /// in, and then subtracting the base. | ||
| 25 | pub const Register = enum(u8) { | ||
| 26 | // 0 through 15, 64-bit registers. 8-15 are extended. | ||
| 27 | // id is just the int value. | ||
| 28 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, | ||
| 29 | r8, r9, r10, r11, r12, r13, r14, r15, | ||
| 30 | |||
| 31 | // 16 through 31, 32-bit registers. 24-31 are extended. | ||
| 32 | // id is int value - 16. | ||
| 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | ||
| 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | ||
| 35 | |||
| 36 | // 32-47, 16-bit registers. 40-47 are extended. | ||
| 37 | // id is int value - 32. | ||
| 38 | ax, cx, dx, bx, sp, bp, si, di, | ||
| 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | ||
| 40 | |||
| 41 | // 48-63, 8-bit registers. 56-63 are extended. | ||
| 42 | // id is int value - 48. | ||
| 43 | al, cl, dl, bl, ah, ch, dh, bh, | ||
| 44 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, | ||
| 45 | |||
| 46 | /// Returns the bit-width of the register. | ||
| 47 | pub fn size(self: Register) u7 { | ||
| 48 | return switch (@enumToInt(self)) { | ||
| 49 | 0...15 => 64, | ||
| 50 | 16...31 => 32, | ||
| 51 | 32...47 => 16, | ||
| 52 | 48...64 => 8, | ||
| 53 | else => unreachable, | ||
| 54 | }; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Returns whether the register is *extended*. Extended registers are the | ||
| 58 | /// new registers added with amd64, r8 through r15. This also includes any | ||
| 59 | /// other variant of access to those registers, such as r8b, r15d, and so | ||
| 60 | /// on. This is needed because access to these registers requires special | ||
| 61 | /// handling via the REX prefix, via the B or R bits, depending on context. | ||
| 62 | pub fn isExtended(self: Register) bool { | ||
| 63 | return @enumToInt(self) & 0x08 != 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | /// This returns the 4-bit register ID, which is used in practically every | ||
| 67 | /// opcode. Note that bit 3 (the highest bit) is *never* used directly in | ||
| 68 | /// an instruction (@see isExtended), and requires special handling. The | ||
| 69 | /// lower three bits are often embedded directly in instructions (such as | ||
| 70 | /// the B8 variant of moves), or used in R/M bytes. | ||
| 71 | pub fn id(self: Register) u4 { | ||
| 72 | return @truncate(u4, @enumToInt(self)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Like id, but only returns the lower 3 bits. | ||
| 76 | pub fn low_id(self: Register) u3 { | ||
| 77 | return @truncate(u3, @enumToInt(self)); | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Returns the index into `callee_preserved_regs`. | ||
| 81 | pub fn allocIndex(self: Register) ?u4 { | ||
| 82 | return switch (self) { | ||
| 83 | .rax, .eax, .ax, .al => 0, | ||
| 84 | .rcx, .ecx, .cx, .cl => 1, | ||
| 85 | .rdx, .edx, .dx, .dl => 2, | ||
| 86 | .rsi, .esi, .si => 3, | ||
| 87 | .rdi, .edi, .di => 4, | ||
| 88 | .r8, .r8d, .r8w, .r8b => 5, | ||
| 89 | .r9, .r9d, .r9w, .r9b => 6, | ||
| 90 | .r10, .r10d, .r10w, .r10b => 7, | ||
| 91 | .r11, .r11d, .r11w, .r11b => 8, | ||
| 92 | else => null, | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Convert from any register to its 64 bit alias. | ||
| 97 | pub fn to64(self: Register) Register { | ||
| 98 | return @intToEnum(Register, self.id()); | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Convert from any register to its 32 bit alias. | ||
| 102 | pub fn to32(self: Register) Register { | ||
| 103 | return @intToEnum(Register, @as(u8, self.id()) + 16); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Convert from any register to its 16 bit alias. | ||
| 107 | pub fn to16(self: Register) Register { | ||
| 108 | return @intToEnum(Register, @as(u8, self.id()) + 32); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Convert from any register to its 8 bit alias. | ||
| 112 | pub fn to8(self: Register) Register { | ||
| 113 | return @intToEnum(Register, @as(u8, self.id()) + 48); | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 117 | return switch (self.to64()) { | ||
| 118 | .rax => DW.OP.reg0, | ||
| 119 | .rdx => DW.OP.reg1, | ||
| 120 | .rcx => DW.OP.reg2, | ||
| 121 | .rbx => DW.OP.reg3, | ||
| 122 | .rsi => DW.OP.reg4, | ||
| 123 | .rdi => DW.OP.reg5, | ||
| 124 | .rbp => DW.OP.reg6, | ||
| 125 | .rsp => DW.OP.reg7, | ||
| 126 | |||
| 127 | .r8 => DW.OP.reg8, | ||
| 128 | .r9 => DW.OP.reg9, | ||
| 129 | .r10 => DW.OP.reg10, | ||
| 130 | .r11 => DW.OP.reg11, | ||
| 131 | .r12 => DW.OP.reg12, | ||
| 132 | .r13 => DW.OP.reg13, | ||
| 133 | .r14 => DW.OP.reg14, | ||
| 134 | .r15 => DW.OP.reg15, | ||
| 135 | |||
| 136 | else => unreachable, | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | }; | ||
| 140 | |||
| 141 | // zig fmt: on | ||
| 142 | |||
| 143 | /// These registers belong to the called function. | ||
| 144 | pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | ||
| 145 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | ||
| 146 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | ||
| 147 | |||
| 148 | /// Encoding helper functions for x86_64 instructions | ||
| 149 | /// | ||
| 150 | /// Many of these helpers do very little, but they can help make things | ||
| 151 | /// slightly more readable with more descriptive field names / function names. | ||
| 152 | /// | ||
| 153 | /// Some of them also have asserts to ensure that we aren't doing dumb things. | ||
| 154 | /// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, | ||
| 155 | /// you need to encode it with an SIB byte. | ||
| 156 | /// | ||
| 157 | /// Note that ALL of these helper functions will assume capacity, | ||
| 158 | /// so ensure that the `code` has sufficient capacity before using them. | ||
| 159 | /// The `init` method is the recommended way to ensure capacity. | ||
| 160 | pub const Encoder = struct { | ||
| 161 | /// Non-owning reference to the code array | ||
| 162 | code: *ArrayList(u8), | ||
| 163 | |||
| 164 | const Self = @This(); | ||
| 165 | |||
| 166 | /// Wrap `code` in Encoder to make it easier to call these helper functions | ||
| 167 | /// | ||
| 168 | /// maximum_inst_size should contain the maximum number of bytes | ||
| 169 | /// that the encoded instruction will take. | ||
| 170 | /// This is because the helper functions will assume capacity | ||
| 171 | /// in order to avoid bounds checking. | ||
| 172 | pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self { | ||
| 173 | try code.ensureUnusedCapacity(maximum_inst_size); | ||
| 174 | return Self{ .code = code }; | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Directly write a number to the code array with big endianness | ||
| 178 | pub fn writeIntBig(self: Self, comptime T: type, value: T) void { | ||
| 179 | mem.writeIntBig( | ||
| 180 | T, | ||
| 181 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | ||
| 182 | value, | ||
| 183 | ); | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Directly write a number to the code array with little endianness | ||
| 187 | pub fn writeIntLittle(self: Self, comptime T: type, value: T) void { | ||
| 188 | mem.writeIntLittle( | ||
| 189 | T, | ||
| 190 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | ||
| 191 | value, | ||
| 192 | ); | ||
| 193 | } | ||
| 194 | |||
| 195 | // -------- | ||
| 196 | // Prefixes | ||
| 197 | // -------- | ||
| 198 | |||
| 199 | pub const LegacyPrefixes = packed struct { | ||
| 200 | /// LOCK | ||
| 201 | prefix_f0: bool = false, | ||
| 202 | /// REPNZ, REPNE, REP, Scalar Double-precision | ||
| 203 | prefix_f2: bool = false, | ||
| 204 | /// REPZ, REPE, REP, Scalar Single-precision | ||
| 205 | prefix_f3: bool = false, | ||
| 206 | |||
| 207 | /// CS segment override or Branch not taken | ||
| 208 | prefix_2e: bool = false, | ||
| 209 | /// DS segment override | ||
| 210 | prefix_36: bool = false, | ||
| 211 | /// ES segment override | ||
| 212 | prefix_26: bool = false, | ||
| 213 | /// FS segment override | ||
| 214 | prefix_64: bool = false, | ||
| 215 | /// GS segment override | ||
| 216 | prefix_65: bool = false, | ||
| 217 | |||
| 218 | /// Branch taken | ||
| 219 | prefix_3e: bool = false, | ||
| 220 | |||
| 221 | /// Operand size override (enables 16 bit operation) | ||
| 222 | prefix_66: bool = false, | ||
| 223 | |||
| 224 | /// Address size override (enables 16 bit address size) | ||
| 225 | prefix_67: bool = false, | ||
| 226 | |||
| 227 | padding: u5 = 0, | ||
| 228 | }; | ||
| 229 | |||
| 230 | /// Encodes legacy prefixes | ||
| 231 | pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { | ||
| 232 | if (@bitCast(u16, prefixes) != 0) { | ||
| 233 | // Hopefully this path isn't taken very often, so we'll do it the slow way for now | ||
| 234 | |||
| 235 | // LOCK | ||
| 236 | if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); | ||
| 237 | // REPNZ, REPNE, REP, Scalar Double-precision | ||
| 238 | if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); | ||
| 239 | // REPZ, REPE, REP, Scalar Single-precision | ||
| 240 | if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); | ||
| 241 | |||
| 242 | // CS segment override or Branch not taken | ||
| 243 | if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); | ||
| 244 | // DS segment override | ||
| 245 | if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); | ||
| 246 | // ES segment override | ||
| 247 | if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); | ||
| 248 | // FS segment override | ||
| 249 | if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); | ||
| 250 | // GS segment override | ||
| 251 | if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); | ||
| 252 | |||
| 253 | // Branch taken | ||
| 254 | if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); | ||
| 255 | |||
| 256 | // Operand size override | ||
| 257 | if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); | ||
| 258 | |||
| 259 | // Address size override | ||
| 260 | if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Use 16 bit operand size | ||
| 265 | /// | ||
| 266 | /// Note that this flag is overridden by REX.W, if both are present. | ||
| 267 | pub fn prefix16BitMode(self: Self) void { | ||
| 268 | self.code.appendAssumeCapacity(0x66); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | ||
| 272 | pub const Rex = struct { | ||
| 273 | /// Wide, enables 64-bit operation | ||
| 274 | w: bool = false, | ||
| 275 | /// Extends the reg field in the ModR/M byte | ||
| 276 | r: bool = false, | ||
| 277 | /// Extends the index field in the SIB byte | ||
| 278 | x: bool = false, | ||
| 279 | /// Extends the r/m field in the ModR/M byte, | ||
| 280 | /// or the base field in the SIB byte, | ||
| 281 | /// or the reg field in the Opcode byte | ||
| 282 | b: bool = false, | ||
| 283 | }; | ||
| 284 | |||
| 285 | /// Encodes a REX prefix byte given all the fields | ||
| 286 | /// | ||
| 287 | /// Use this byte whenever you need 64 bit operation, | ||
| 288 | /// or one of reg, index, r/m, base, or opcode-reg might be extended. | ||
| 289 | /// | ||
| 290 | /// See struct `Rex` for a description of each field. | ||
| 291 | /// | ||
| 292 | /// Does not add a prefix byte if none of the fields are set! | ||
| 293 | pub fn rex(self: Self, byte: Rex) void { | ||
| 294 | var value: u8 = 0b0100_0000; | ||
| 295 | |||
| 296 | if (byte.w) value |= 0b1000; | ||
| 297 | if (byte.r) value |= 0b0100; | ||
| 298 | if (byte.x) value |= 0b0010; | ||
| 299 | if (byte.b) value |= 0b0001; | ||
| 300 | |||
| 301 | if (value != 0b0100_0000) { | ||
| 302 | self.code.appendAssumeCapacity(value); | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | // ------ | ||
| 307 | // Opcode | ||
| 308 | // ------ | ||
| 309 | |||
| 310 | /// Encodes a 1 byte opcode | ||
| 311 | pub fn opcode_1byte(self: Self, opcode: u8) void { | ||
| 312 | self.code.appendAssumeCapacity(opcode); | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Encodes a 2 byte opcode | ||
| 316 | /// | ||
| 317 | /// e.g. IMUL has the opcode 0x0f 0xaf, so you use | ||
| 318 | /// | ||
| 319 | /// encoder.opcode_2byte(0x0f, 0xaf); | ||
| 320 | pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { | ||
| 321 | self.code.appendAssumeCapacity(prefix); | ||
| 322 | self.code.appendAssumeCapacity(opcode); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Encodes a 1 byte opcode with a reg field | ||
| 326 | /// | ||
| 327 | /// Remember to add a REX prefix byte if reg is extended! | ||
| 328 | pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { | ||
| 329 | assert(opcode & 0b111 == 0); | ||
| 330 | self.code.appendAssumeCapacity(opcode | reg); | ||
| 331 | } | ||
| 332 | |||
| 333 | // ------ | ||
| 334 | // ModR/M | ||
| 335 | // ------ | ||
| 336 | |||
| 337 | /// Construct a ModR/M byte given all the fields | ||
| 338 | /// | ||
| 339 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 340 | pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { | ||
| 341 | self.code.appendAssumeCapacity( | ||
| 342 | @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, | ||
| 343 | ); | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Construct a ModR/M byte using direct r/m addressing | ||
| 347 | /// r/m effective address: r/m | ||
| 348 | /// | ||
| 349 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 350 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 351 | pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 352 | self.modRm(0b11, reg_or_opx, rm); | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Construct a ModR/M byte using indirect r/m addressing | ||
| 356 | /// r/m effective address: [r/m] | ||
| 357 | /// | ||
| 358 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 359 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 360 | pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 361 | assert(rm != 4 and rm != 5); | ||
| 362 | self.modRm(0b00, reg_or_opx, rm); | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Construct a ModR/M byte using indirect SIB addressing | ||
| 366 | /// r/m effective address: [SIB] | ||
| 367 | /// | ||
| 368 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 369 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 370 | pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { | ||
| 371 | self.modRm(0b00, reg_or_opx, 0b100); | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Construct a ModR/M byte using RIP-relative addressing | ||
| 375 | /// r/m effective address: [RIP + disp32] | ||
| 376 | /// | ||
| 377 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 378 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 379 | pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { | ||
| 380 | self.modRm(0b00, reg_or_opx, 0b101); | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Construct a ModR/M byte using indirect r/m with a 8bit displacement | ||
| 384 | /// r/m effective address: [r/m + disp8] | ||
| 385 | /// | ||
| 386 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 387 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 388 | pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 389 | assert(rm != 4); | ||
| 390 | self.modRm(0b01, reg_or_opx, rm); | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Construct a ModR/M byte using indirect SIB with a 8bit displacement | ||
| 394 | /// r/m effective address: [SIB + disp8] | ||
| 395 | /// | ||
| 396 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 397 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 398 | pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { | ||
| 399 | self.modRm(0b01, reg_or_opx, 0b100); | ||
| 400 | } | ||
| 401 | |||
| 402 | /// Construct a ModR/M byte using indirect r/m with a 32bit displacement | ||
| 403 | /// r/m effective address: [r/m + disp32] | ||
| 404 | /// | ||
| 405 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 406 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 407 | pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 408 | assert(rm != 4); | ||
| 409 | self.modRm(0b10, reg_or_opx, rm); | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Construct a ModR/M byte using indirect SIB with a 32bit displacement | ||
| 413 | /// r/m effective address: [SIB + disp32] | ||
| 414 | /// | ||
| 415 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 416 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 417 | pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { | ||
| 418 | self.modRm(0b10, reg_or_opx, 0b100); | ||
| 419 | } | ||
| 420 | |||
| 421 | // --- | ||
| 422 | // SIB | ||
| 423 | // --- | ||
| 424 | |||
| 425 | /// Construct a SIB byte given all the fields | ||
| 426 | /// | ||
| 427 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 428 | pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 429 | self.code.appendAssumeCapacity( | ||
| 430 | @as(u8, scale) << 6 | @as(u8, index) << 3 | base, | ||
| 431 | ); | ||
| 432 | } | ||
| 433 | |||
| 434 | /// Construct a SIB byte with scale * index + base, no frills. | ||
| 435 | /// r/m effective address: [base + scale * index] | ||
| 436 | /// | ||
| 437 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 438 | pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 439 | assert(base != 5); | ||
| 440 | |||
| 441 | self.sib(scale, index, base); | ||
| 442 | } | ||
| 443 | |||
| 444 | /// Construct a SIB byte with scale * index + disp32 | ||
| 445 | /// r/m effective address: [scale * index + disp32] | ||
| 446 | /// | ||
| 447 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 448 | pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { | ||
| 449 | assert(index != 4); | ||
| 450 | |||
| 451 | // scale is actually ignored | ||
| 452 | // index = 4 means no index | ||
| 453 | // base = 5 means no base, if mod == 0. | ||
| 454 | self.sib(scale, index, 5); | ||
| 455 | } | ||
| 456 | |||
| 457 | /// Construct a SIB byte with just base | ||
| 458 | /// r/m effective address: [base] | ||
| 459 | /// | ||
| 460 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 461 | pub fn sib_base(self: Self, base: u3) void { | ||
| 462 | assert(base != 5); | ||
| 463 | |||
| 464 | // scale is actually ignored | ||
| 465 | // index = 4 means no index | ||
| 466 | self.sib(0, 4, base); | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Construct a SIB byte with just disp32 | ||
| 470 | /// r/m effective address: [disp32] | ||
| 471 | /// | ||
| 472 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 473 | pub fn sib_disp32(self: Self) void { | ||
| 474 | // scale is actually ignored | ||
| 475 | // index = 4 means no index | ||
| 476 | // base = 5 means no base, if mod == 0. | ||
| 477 | self.sib(0, 4, 5); | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Construct a SIB byte with scale * index + base + disp8 | ||
| 481 | /// r/m effective address: [base + scale * index + disp8] | ||
| 482 | /// | ||
| 483 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 484 | pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 485 | self.sib(scale, index, base); | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Construct a SIB byte with base + disp8, no index | ||
| 489 | /// r/m effective address: [base + disp8] | ||
| 490 | /// | ||
| 491 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 492 | pub fn sib_baseDisp8(self: Self, base: u3) void { | ||
| 493 | // scale is ignored | ||
| 494 | // index = 4 means no index | ||
| 495 | self.sib(0, 4, base); | ||
| 496 | } | ||
| 497 | |||
| 498 | /// Construct a SIB byte with scale * index + base + disp32 | ||
| 499 | /// r/m effective address: [base + scale * index + disp32] | ||
| 500 | /// | ||
| 501 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 502 | pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 503 | self.sib(scale, index, base); | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Construct a SIB byte with base + disp32, no index | ||
| 507 | /// r/m effective address: [base + disp32] | ||
| 508 | /// | ||
| 509 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 510 | pub fn sib_baseDisp32(self: Self, base: u3) void { | ||
| 511 | // scale is ignored | ||
| 512 | // index = 4 means no index | ||
| 513 | self.sib(0, 4, base); | ||
| 514 | } | ||
| 515 | |||
| 516 | // ------------------------- | ||
| 517 | // Trivial (no bit fiddling) | ||
| 518 | // ------------------------- | ||
| 519 | |||
| 520 | /// Encode an 8 bit immediate | ||
| 521 | /// | ||
| 522 | /// It is sign-extended to 64 bits by the cpu. | ||
| 523 | pub fn imm8(self: Self, imm: i8) void { | ||
| 524 | self.code.appendAssumeCapacity(@bitCast(u8, imm)); | ||
| 525 | } | ||
| 526 | |||
| 527 | /// Encode an 8 bit displacement | ||
| 528 | /// | ||
| 529 | /// It is sign-extended to 64 bits by the cpu. | ||
| 530 | pub fn disp8(self: Self, disp: i8) void { | ||
| 531 | self.code.appendAssumeCapacity(@bitCast(u8, disp)); | ||
| 532 | } | ||
| 533 | |||
| 534 | /// Encode an 16 bit immediate | ||
| 535 | /// | ||
| 536 | /// It is sign-extended to 64 bits by the cpu. | ||
| 537 | pub fn imm16(self: Self, imm: i16) void { | ||
| 538 | self.writeIntLittle(i16, imm); | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Encode an 32 bit immediate | ||
| 542 | /// | ||
| 543 | /// It is sign-extended to 64 bits by the cpu. | ||
| 544 | pub fn imm32(self: Self, imm: i32) void { | ||
| 545 | self.writeIntLittle(i32, imm); | ||
| 546 | } | ||
| 547 | |||
| 548 | /// Encode an 32 bit displacement | ||
| 549 | /// | ||
| 550 | /// It is sign-extended to 64 bits by the cpu. | ||
| 551 | pub fn disp32(self: Self, disp: i32) void { | ||
| 552 | self.writeIntLittle(i32, disp); | ||
| 553 | } | ||
| 554 | |||
| 555 | /// Encode an 64 bit immediate | ||
| 556 | /// | ||
| 557 | /// It is sign-extended to 64 bits by the cpu. | ||
| 558 | pub fn imm64(self: Self, imm: u64) void { | ||
| 559 | self.writeIntLittle(u64, imm); | ||
| 560 | } | ||
| 561 | }; | ||
| 562 | |||
| 563 | test "x86_64 Encoder helpers" { | ||
| 564 | var code = ArrayList(u8).init(testing.allocator); | ||
| 565 | defer code.deinit(); | ||
| 566 | |||
| 567 | // simple integer multiplication | ||
| 568 | |||
| 569 | // imul eax,edi | ||
| 570 | // 0faf c7 | ||
| 571 | { | ||
| 572 | try code.resize(0); | ||
| 573 | const encoder = try Encoder.init(&code, 4); | ||
| 574 | encoder.rex(.{ | ||
| 575 | .r = Register.eax.isExtended(), | ||
| 576 | .b = Register.edi.isExtended(), | ||
| 577 | }); | ||
| 578 | encoder.opcode_2byte(0x0f, 0xaf); | ||
| 579 | encoder.modRm_direct( | ||
| 580 | Register.eax.low_id(), | ||
| 581 | Register.edi.low_id(), | ||
| 582 | ); | ||
| 583 | |||
| 584 | try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); | ||
| 585 | } | ||
| 586 | |||
| 587 | // simple mov | ||
| 588 | |||
| 589 | // mov eax,edi | ||
| 590 | // 89 f8 | ||
| 591 | { | ||
| 592 | try code.resize(0); | ||
| 593 | const encoder = try Encoder.init(&code, 3); | ||
| 594 | encoder.rex(.{ | ||
| 595 | .r = Register.edi.isExtended(), | ||
| 596 | .b = Register.eax.isExtended(), | ||
| 597 | }); | ||
| 598 | encoder.opcode_1byte(0x89); | ||
| 599 | encoder.modRm_direct( | ||
| 600 | Register.edi.low_id(), | ||
| 601 | Register.eax.low_id(), | ||
| 602 | ); | ||
| 603 | |||
| 604 | try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); | ||
| 605 | } | ||
| 606 | |||
| 607 | // signed integer addition of 32-bit sign extended immediate to 64 bit register | ||
| 608 | |||
| 609 | // add rcx, 2147483647 | ||
| 610 | // | ||
| 611 | // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding | ||
| 612 | // | ||
| 613 | // 48 : REX.W set for 64 bit operand (*r*cx) | ||
| 614 | // 81 : opcode for "<arithmetic> with immediate" | ||
| 615 | // c1 : id = rcx, | ||
| 616 | // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) | ||
| 617 | // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD | ||
| 618 | // : 001 <-- 001 is rcx | ||
| 619 | // ffffff7f : 2147483647 | ||
| 620 | { | ||
| 621 | try code.resize(0); | ||
| 622 | const encoder = try Encoder.init(&code, 7); | ||
| 623 | encoder.rex(.{ .w = true }); // use 64 bit operation | ||
| 624 | encoder.opcode_1byte(0x81); | ||
| 625 | encoder.modRm_direct( | ||
| 626 | 0, | ||
| 627 | Register.rcx.low_id(), | ||
| 628 | ); | ||
| 629 | encoder.imm32(2147483647); | ||
| 630 | |||
| 631 | try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | // TODO add these registers to the enum and populate dwarfLocOp | ||
| 636 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | ||
| 637 | // RA = (16, "RA"), | ||
| 638 | // | ||
| 639 | // XMM0 = (17, "xmm0"), | ||
| 640 | // XMM1 = (18, "xmm1"), | ||
| 641 | // XMM2 = (19, "xmm2"), | ||
| 642 | // XMM3 = (20, "xmm3"), | ||
| 643 | // XMM4 = (21, "xmm4"), | ||
| 644 | // XMM5 = (22, "xmm5"), | ||
| 645 | // XMM6 = (23, "xmm6"), | ||
| 646 | // XMM7 = (24, "xmm7"), | ||
| 647 | // | ||
| 648 | // XMM8 = (25, "xmm8"), | ||
| 649 | // XMM9 = (26, "xmm9"), | ||
| 650 | // XMM10 = (27, "xmm10"), | ||
| 651 | // XMM11 = (28, "xmm11"), | ||
| 652 | // XMM12 = (29, "xmm12"), | ||
| 653 | // XMM13 = (30, "xmm13"), | ||
| 654 | // XMM14 = (31, "xmm14"), | ||
| 655 | // XMM15 = (32, "xmm15"), | ||
| 656 | // | ||
| 657 | // ST0 = (33, "st0"), | ||
| 658 | // ST1 = (34, "st1"), | ||
| 659 | // ST2 = (35, "st2"), | ||
| 660 | // ST3 = (36, "st3"), | ||
| 661 | // ST4 = (37, "st4"), | ||
| 662 | // ST5 = (38, "st5"), | ||
| 663 | // ST6 = (39, "st6"), | ||
| 664 | // ST7 = (40, "st7"), | ||
| 665 | // | ||
| 666 | // MM0 = (41, "mm0"), | ||
| 667 | // MM1 = (42, "mm1"), | ||
| 668 | // MM2 = (43, "mm2"), | ||
| 669 | // MM3 = (44, "mm3"), | ||
| 670 | // MM4 = (45, "mm4"), | ||
| 671 | // MM5 = (46, "mm5"), | ||
| 672 | // MM6 = (47, "mm6"), | ||
| 673 | // MM7 = (48, "mm7"), | ||
| 674 | // | ||
| 675 | // RFLAGS = (49, "rFLAGS"), | ||
| 676 | // ES = (50, "es"), | ||
| 677 | // CS = (51, "cs"), | ||
| 678 | // SS = (52, "ss"), | ||
| 679 | // DS = (53, "ds"), | ||
| 680 | // FS = (54, "fs"), | ||
| 681 | // GS = (55, "gs"), | ||
| 682 | // | ||
| 683 | // FS_BASE = (58, "fs.base"), | ||
| 684 | // GS_BASE = (59, "gs.base"), | ||
| 685 | // | ||
| 686 | // TR = (62, "tr"), | ||
| 687 | // LDTR = (63, "ldtr"), | ||
| 688 | // MXCSR = (64, "mxcsr"), | ||
| 689 | // FCW = (65, "fcw"), | ||
| 690 | // FSW = (66, "fsw"), | ||
| 691 | // | ||
| 692 | // XMM16 = (67, "xmm16"), | ||
| 693 | // XMM17 = (68, "xmm17"), | ||
| 694 | // XMM18 = (69, "xmm18"), | ||
| 695 | // XMM19 = (70, "xmm19"), | ||
| 696 | // XMM20 = (71, "xmm20"), | ||
| 697 | // XMM21 = (72, "xmm21"), | ||
| 698 | // XMM22 = (73, "xmm22"), | ||
| 699 | // XMM23 = (74, "xmm23"), | ||
| 700 | // XMM24 = (75, "xmm24"), | ||
| 701 | // XMM25 = (76, "xmm25"), | ||
| 702 | // XMM26 = (77, "xmm26"), | ||
| 703 | // XMM27 = (78, "xmm27"), | ||
| 704 | // XMM28 = (79, "xmm28"), | ||
| 705 | // XMM29 = (80, "xmm29"), | ||
| 706 | // XMM30 = (81, "xmm30"), | ||
| 707 | // XMM31 = (82, "xmm31"), | ||
| 708 | // | ||
| 709 | // K0 = (118, "k0"), | ||
| 710 | // K1 = (119, "k1"), | ||
| 711 | // K2 = (120, "k2"), | ||
| 712 | // K3 = (121, "k3"), | ||
| 713 | // K4 = (122, "k4"), | ||
| 714 | // K5 = (123, "k5"), | ||
| 715 | // K6 = (124, "k6"), | ||
| 716 | // K7 = (125, "k7"), | ||
src/codegen.zig+24-24| ... | @@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen); | ... | @@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen); |
| 21 | const build_options = @import("build_options"); | 21 | const build_options = @import("build_options"); |
| 22 | const RegisterManager = @import("register_manager.zig").RegisterManager; | 22 | const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 23 | 23 | ||
| 24 | const X8664Encoder = @import("codegen/x86_64.zig").Encoder; | 24 | const X8664Encoder = @import("arch/x86_64/bits.zig").Encoder; |
| 25 | 25 | ||
| 26 | pub const FnResult = union(enum) { | 26 | pub const FnResult = union(enum) { |
| 27 | /// The `code` parameter passed to `generateSymbol` has the value appended. | 27 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| ... | @@ -470,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -470,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 470 | /// A branch in the ARM instruction set | 470 | /// A branch in the ARM instruction set |
| 471 | arm_branch: struct { | 471 | arm_branch: struct { |
| 472 | pos: usize, | 472 | pos: usize, |
| 473 | cond: @import("codegen/arm.zig").Condition, | 473 | cond: @import("arch/arm/bits.zig").Condition, |
| 474 | }, | 474 | }, |
| 475 | }; | 475 | }; |
| 476 | 476 | ||
| ... | @@ -5336,11 +5336,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -5336,11 +5336,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 5336 | } | 5336 | } |
| 5337 | 5337 | ||
| 5338 | const Register = switch (arch) { | 5338 | const Register = switch (arch) { |
| 5339 | .i386 => @import("codegen/x86.zig").Register, | 5339 | .i386 => @import("arch/x86/bits.zig").Register, |
| 5340 | .x86_64 => @import("codegen/x86_64.zig").Register, | 5340 | .x86_64 => @import("arch/x86_64/bits.zig").Register, |
| 5341 | .riscv64 => @import("codegen/riscv64.zig").Register, | 5341 | .riscv64 => @import("arch/riscv64/bits.zig").Register, |
| 5342 | .arm, .armeb => @import("codegen/arm.zig").Register, | 5342 | .arm, .armeb => @import("arch/arm/bits.zig").Register, |
| 5343 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register, | 5343 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register, |
| 5344 | else => enum { | 5344 | else => enum { |
| 5345 | dummy, | 5345 | dummy, |
| 5346 | 5346 | ||
| ... | @@ -5352,39 +5352,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -5352,39 +5352,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 5352 | }; | 5352 | }; |
| 5353 | 5353 | ||
| 5354 | const Instruction = switch (arch) { | 5354 | const Instruction = switch (arch) { |
| 5355 | .riscv64 => @import("codegen/riscv64.zig").Instruction, | 5355 | .riscv64 => @import("arch/riscv64/bits.zig").Instruction, |
| 5356 | .arm, .armeb => @import("codegen/arm.zig").Instruction, | 5356 | .arm, .armeb => @import("arch/arm/bits.zig").Instruction, |
| 5357 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction, | 5357 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction, |
| 5358 | else => void, | 5358 | else => void, |
| 5359 | }; | 5359 | }; |
| 5360 | 5360 | ||
| 5361 | const Condition = switch (arch) { | 5361 | const Condition = switch (arch) { |
| 5362 | .arm, .armeb => @import("codegen/arm.zig").Condition, | 5362 | .arm, .armeb => @import("arch/arm/bits.zig").Condition, |
| 5363 | else => void, | 5363 | else => void, |
| 5364 | }; | 5364 | }; |
| 5365 | 5365 | ||
| 5366 | const callee_preserved_regs = switch (arch) { | 5366 | const callee_preserved_regs = switch (arch) { |
| 5367 | .i386 => @import("codegen/x86.zig").callee_preserved_regs, | 5367 | .i386 => @import("arch/x86/bits.zig").callee_preserved_regs, |
| 5368 | .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs, | 5368 | .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs, |
| 5369 | .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs, | 5369 | .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs, |
| 5370 | .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs, | 5370 | .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs, |
| 5371 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs, | 5371 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs, |
| 5372 | else => [_]Register{}, | 5372 | else => [_]Register{}, |
| 5373 | }; | 5373 | }; |
| 5374 | 5374 | ||
| 5375 | const c_abi_int_param_regs = switch (arch) { | 5375 | const c_abi_int_param_regs = switch (arch) { |
| 5376 | .i386 => @import("codegen/x86.zig").c_abi_int_param_regs, | 5376 | .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs, |
| 5377 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs, | 5377 | .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs, |
| 5378 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs, | 5378 | .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs, |
| 5379 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs, | 5379 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs, |
| 5380 | else => [_]Register{}, | 5380 | else => [_]Register{}, |
| 5381 | }; | 5381 | }; |
| 5382 | 5382 | ||
| 5383 | const c_abi_int_return_regs = switch (arch) { | 5383 | const c_abi_int_return_regs = switch (arch) { |
| 5384 | .i386 => @import("codegen/x86.zig").c_abi_int_return_regs, | 5384 | .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs, |
| 5385 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs, | 5385 | .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs, |
| 5386 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs, | 5386 | .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs, |
| 5387 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs, | 5387 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs, |
| 5388 | else => [_]Register{}, | 5388 | else => [_]Register{}, |
| 5389 | }; | 5389 | }; |
| 5390 | 5390 |
src/codegen/aarch64.zig deleted-1230| ... | @@ -1,1230 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | // zig fmt: off | ||
| 7 | |||
| 8 | /// General purpose registers in the AArch64 instruction set | ||
| 9 | pub const Register = enum(u6) { | ||
| 10 | // 64-bit registers | ||
| 11 | x0, x1, x2, x3, x4, x5, x6, x7, | ||
| 12 | x8, x9, x10, x11, x12, x13, x14, x15, | ||
| 13 | x16, x17, x18, x19, x20, x21, x22, x23, | ||
| 14 | x24, x25, x26, x27, x28, x29, x30, xzr, | ||
| 15 | |||
| 16 | // 32-bit registers | ||
| 17 | w0, w1, w2, w3, w4, w5, w6, w7, | ||
| 18 | w8, w9, w10, w11, w12, w13, w14, w15, | ||
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | ||
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | ||
| 21 | |||
| 22 | pub const sp = Register.xzr; | ||
| 23 | |||
| 24 | pub fn id(self: Register) u5 { | ||
| 25 | return @truncate(u5, @enumToInt(self)); | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Returns the bit-width of the register. | ||
| 29 | pub fn size(self: Register) u7 { | ||
| 30 | return switch (@enumToInt(self)) { | ||
| 31 | 0...31 => 64, | ||
| 32 | 32...63 => 32, | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Convert from any register to its 64 bit alias. | ||
| 37 | pub fn to64(self: Register) Register { | ||
| 38 | return @intToEnum(Register, self.id()); | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Convert from any register to its 32 bit alias. | ||
| 42 | pub fn to32(self: Register) Register { | ||
| 43 | return @intToEnum(Register, @as(u6, self.id()) + 32); | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Returns the index into `callee_preserved_regs`. | ||
| 47 | pub fn allocIndex(self: Register) ?u4 { | ||
| 48 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 49 | if (self.id() == cpreg.id()) return i; | ||
| 50 | } | ||
| 51 | return null; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 55 | return @as(u8, self.id()) + DW.OP.reg0; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | // zig fmt: on | ||
| 60 | |||
| 61 | pub const callee_preserved_regs = [_]Register{ | ||
| 62 | .x19, .x20, .x21, .x22, .x23, | ||
| 63 | .x24, .x25, .x26, .x27, .x28, | ||
| 64 | }; | ||
| 65 | |||
| 66 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 67 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 68 | |||
| 69 | test "Register.id" { | ||
| 70 | try testing.expectEqual(@as(u5, 0), Register.x0.id()); | ||
| 71 | try testing.expectEqual(@as(u5, 0), Register.w0.id()); | ||
| 72 | |||
| 73 | try testing.expectEqual(@as(u5, 31), Register.xzr.id()); | ||
| 74 | try testing.expectEqual(@as(u5, 31), Register.wzr.id()); | ||
| 75 | |||
| 76 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | ||
| 77 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | ||
| 78 | } | ||
| 79 | |||
| 80 | test "Register.size" { | ||
| 81 | try testing.expectEqual(@as(u7, 64), Register.x19.size()); | ||
| 82 | try testing.expectEqual(@as(u7, 32), Register.w3.size()); | ||
| 83 | } | ||
| 84 | |||
| 85 | test "Register.to64/to32" { | ||
| 86 | try testing.expectEqual(Register.x0, Register.w0.to64()); | ||
| 87 | try testing.expectEqual(Register.x0, Register.x0.to64()); | ||
| 88 | |||
| 89 | try testing.expectEqual(Register.w3, Register.w3.to32()); | ||
| 90 | try testing.expectEqual(Register.w3, Register.x3.to32()); | ||
| 91 | } | ||
| 92 | |||
| 93 | // zig fmt: off | ||
| 94 | |||
| 95 | /// Scalar floating point registers in the aarch64 instruction set | ||
| 96 | pub const FloatingPointRegister = enum(u8) { | ||
| 97 | // 128-bit registers | ||
| 98 | q0, q1, q2, q3, q4, q5, q6, q7, | ||
| 99 | q8, q9, q10, q11, q12, q13, q14, q15, | ||
| 100 | q16, q17, q18, q19, q20, q21, q22, q23, | ||
| 101 | q24, q25, q26, q27, q28, q29, q30, q31, | ||
| 102 | |||
| 103 | // 64-bit registers | ||
| 104 | d0, d1, d2, d3, d4, d5, d6, d7, | ||
| 105 | d8, d9, d10, d11, d12, d13, d14, d15, | ||
| 106 | d16, d17, d18, d19, d20, d21, d22, d23, | ||
| 107 | d24, d25, d26, d27, d28, d29, d30, d31, | ||
| 108 | |||
| 109 | // 32-bit registers | ||
| 110 | s0, s1, s2, s3, s4, s5, s6, s7, | ||
| 111 | s8, s9, s10, s11, s12, s13, s14, s15, | ||
| 112 | s16, s17, s18, s19, s20, s21, s22, s23, | ||
| 113 | s24, s25, s26, s27, s28, s29, s30, s31, | ||
| 114 | |||
| 115 | // 16-bit registers | ||
| 116 | h0, h1, h2, h3, h4, h5, h6, h7, | ||
| 117 | h8, h9, h10, h11, h12, h13, h14, h15, | ||
| 118 | h16, h17, h18, h19, h20, h21, h22, h23, | ||
| 119 | h24, h25, h26, h27, h28, h29, h30, h31, | ||
| 120 | |||
| 121 | // 8-bit registers | ||
| 122 | b0, b1, b2, b3, b4, b5, b6, b7, | ||
| 123 | b8, b9, b10, b11, b12, b13, b14, b15, | ||
| 124 | b16, b17, b18, b19, b20, b21, b22, b23, | ||
| 125 | b24, b25, b26, b27, b28, b29, b30, b31, | ||
| 126 | |||
| 127 | pub fn id(self: FloatingPointRegister) u5 { | ||
| 128 | return @truncate(u5, @enumToInt(self)); | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Returns the bit-width of the register. | ||
| 132 | pub fn size(self: FloatingPointRegister) u8 { | ||
| 133 | return switch (@enumToInt(self)) { | ||
| 134 | 0...31 => 128, | ||
| 135 | 32...63 => 64, | ||
| 136 | 64...95 => 32, | ||
| 137 | 96...127 => 16, | ||
| 138 | 128...159 => 8, | ||
| 139 | else => unreachable, | ||
| 140 | }; | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Convert from any register to its 128 bit alias. | ||
| 144 | pub fn to128(self: FloatingPointRegister) FloatingPointRegister { | ||
| 145 | return @intToEnum(FloatingPointRegister, self.id()); | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Convert from any register to its 64 bit alias. | ||
| 149 | pub fn to64(self: FloatingPointRegister) FloatingPointRegister { | ||
| 150 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Convert from any register to its 32 bit alias. | ||
| 154 | pub fn to32(self: FloatingPointRegister) FloatingPointRegister { | ||
| 155 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Convert from any register to its 16 bit alias. | ||
| 159 | pub fn to16(self: FloatingPointRegister) FloatingPointRegister { | ||
| 160 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Convert from any register to its 8 bit alias. | ||
| 164 | pub fn to8(self: FloatingPointRegister) FloatingPointRegister { | ||
| 165 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 169 | // zig fmt: on | ||
| 170 | |||
| 171 | test "FloatingPointRegister.id" { | ||
| 172 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); | ||
| 173 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); | ||
| 174 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); | ||
| 175 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); | ||
| 176 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); | ||
| 177 | |||
| 178 | try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); | ||
| 179 | try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); | ||
| 180 | } | ||
| 181 | |||
| 182 | test "FloatingPointRegister.size" { | ||
| 183 | try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); | ||
| 184 | try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); | ||
| 185 | try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); | ||
| 186 | try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); | ||
| 187 | try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); | ||
| 188 | } | ||
| 189 | |||
| 190 | test "FloatingPointRegister.toX" { | ||
| 191 | try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); | ||
| 192 | try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); | ||
| 193 | try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); | ||
| 194 | |||
| 195 | try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); | ||
| 196 | try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); | ||
| 197 | try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); | ||
| 198 | try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Represents an instruction in the AArch64 instruction set | ||
| 202 | pub const Instruction = union(enum) { | ||
| 203 | move_wide_immediate: packed struct { | ||
| 204 | rd: u5, | ||
| 205 | imm16: u16, | ||
| 206 | hw: u2, | ||
| 207 | fixed: u6 = 0b100101, | ||
| 208 | opc: u2, | ||
| 209 | sf: u1, | ||
| 210 | }, | ||
| 211 | pc_relative_address: packed struct { | ||
| 212 | rd: u5, | ||
| 213 | immhi: u19, | ||
| 214 | fixed: u5 = 0b10000, | ||
| 215 | immlo: u2, | ||
| 216 | op: u1, | ||
| 217 | }, | ||
| 218 | load_store_register: packed struct { | ||
| 219 | rt: u5, | ||
| 220 | rn: u5, | ||
| 221 | offset: u12, | ||
| 222 | opc: u2, | ||
| 223 | op1: u2, | ||
| 224 | v: u1, | ||
| 225 | fixed: u3 = 0b111, | ||
| 226 | size: u2, | ||
| 227 | }, | ||
| 228 | load_store_register_pair: packed struct { | ||
| 229 | rt1: u5, | ||
| 230 | rn: u5, | ||
| 231 | rt2: u5, | ||
| 232 | imm7: u7, | ||
| 233 | load: u1, | ||
| 234 | encoding: u2, | ||
| 235 | fixed: u5 = 0b101_0_0, | ||
| 236 | opc: u2, | ||
| 237 | }, | ||
| 238 | load_literal: packed struct { | ||
| 239 | rt: u5, | ||
| 240 | imm19: u19, | ||
| 241 | fixed: u6 = 0b011_0_00, | ||
| 242 | opc: u2, | ||
| 243 | }, | ||
| 244 | exception_generation: packed struct { | ||
| 245 | ll: u2, | ||
| 246 | op2: u3, | ||
| 247 | imm16: u16, | ||
| 248 | opc: u3, | ||
| 249 | fixed: u8 = 0b1101_0100, | ||
| 250 | }, | ||
| 251 | unconditional_branch_register: packed struct { | ||
| 252 | op4: u5, | ||
| 253 | rn: u5, | ||
| 254 | op3: u6, | ||
| 255 | op2: u5, | ||
| 256 | opc: u4, | ||
| 257 | fixed: u7 = 0b1101_011, | ||
| 258 | }, | ||
| 259 | unconditional_branch_immediate: packed struct { | ||
| 260 | imm26: u26, | ||
| 261 | fixed: u5 = 0b00101, | ||
| 262 | op: u1, | ||
| 263 | }, | ||
| 264 | no_operation: packed struct { | ||
| 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, | ||
| 266 | }, | ||
| 267 | logical_shifted_register: packed struct { | ||
| 268 | rd: u5, | ||
| 269 | rn: u5, | ||
| 270 | imm6: u6, | ||
| 271 | rm: u5, | ||
| 272 | n: u1, | ||
| 273 | shift: u2, | ||
| 274 | fixed: u5 = 0b01010, | ||
| 275 | opc: u2, | ||
| 276 | sf: u1, | ||
| 277 | }, | ||
| 278 | add_subtract_immediate: packed struct { | ||
| 279 | rd: u5, | ||
| 280 | rn: u5, | ||
| 281 | imm12: u12, | ||
| 282 | sh: u1, | ||
| 283 | fixed: u6 = 0b100010, | ||
| 284 | s: u1, | ||
| 285 | op: u1, | ||
| 286 | sf: u1, | ||
| 287 | }, | ||
| 288 | conditional_branch: struct { | ||
| 289 | cond: u4, | ||
| 290 | o0: u1, | ||
| 291 | imm19: u19, | ||
| 292 | o1: u1, | ||
| 293 | fixed: u7 = 0b0101010, | ||
| 294 | }, | ||
| 295 | compare_and_branch: struct { | ||
| 296 | rt: u5, | ||
| 297 | imm19: u19, | ||
| 298 | op: u1, | ||
| 299 | fixed: u6 = 0b011010, | ||
| 300 | sf: u1, | ||
| 301 | }, | ||
| 302 | |||
| 303 | pub const Shift = struct { | ||
| 304 | shift: Type = .lsl, | ||
| 305 | amount: u6 = 0, | ||
| 306 | |||
| 307 | pub const Type = enum(u2) { | ||
| 308 | lsl, | ||
| 309 | lsr, | ||
| 310 | asr, | ||
| 311 | ror, | ||
| 312 | }; | ||
| 313 | |||
| 314 | pub const none = Shift{ | ||
| 315 | .shift = .lsl, | ||
| 316 | .amount = 0, | ||
| 317 | }; | ||
| 318 | }; | ||
| 319 | |||
| 320 | pub const Condition = enum(u4) { | ||
| 321 | /// Integer: Equal | ||
| 322 | /// Floating point: Equal | ||
| 323 | eq, | ||
| 324 | /// Integer: Not equal | ||
| 325 | /// Floating point: Not equal or unordered | ||
| 326 | ne, | ||
| 327 | /// Integer: Carry set | ||
| 328 | /// Floating point: Greater than, equal, or unordered | ||
| 329 | cs, | ||
| 330 | /// Integer: Carry clear | ||
| 331 | /// Floating point: Less than | ||
| 332 | cc, | ||
| 333 | /// Integer: Minus, negative | ||
| 334 | /// Floating point: Less than | ||
| 335 | mi, | ||
| 336 | /// Integer: Plus, positive or zero | ||
| 337 | /// Floating point: Greater than, equal, or unordered | ||
| 338 | pl, | ||
| 339 | /// Integer: Overflow | ||
| 340 | /// Floating point: Unordered | ||
| 341 | vs, | ||
| 342 | /// Integer: No overflow | ||
| 343 | /// Floating point: Ordered | ||
| 344 | vc, | ||
| 345 | /// Integer: Unsigned higher | ||
| 346 | /// Floating point: Greater than, or unordered | ||
| 347 | hi, | ||
| 348 | /// Integer: Unsigned lower or same | ||
| 349 | /// Floating point: Less than or equal | ||
| 350 | ls, | ||
| 351 | /// Integer: Signed greater than or equal | ||
| 352 | /// Floating point: Greater than or equal | ||
| 353 | ge, | ||
| 354 | /// Integer: Signed less than | ||
| 355 | /// Floating point: Less than, or unordered | ||
| 356 | lt, | ||
| 357 | /// Integer: Signed greater than | ||
| 358 | /// Floating point: Greater than | ||
| 359 | gt, | ||
| 360 | /// Integer: Signed less than or equal | ||
| 361 | /// Floating point: Less than, equal, or unordered | ||
| 362 | le, | ||
| 363 | /// Integer: Always | ||
| 364 | /// Floating point: Always | ||
| 365 | al, | ||
| 366 | /// Integer: Always | ||
| 367 | /// Floating point: Always | ||
| 368 | nv, | ||
| 369 | }; | ||
| 370 | |||
| 371 | pub fn toU32(self: Instruction) u32 { | ||
| 372 | return switch (self) { | ||
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), | ||
| 374 | .pc_relative_address => |v| @bitCast(u32, v), | ||
| 375 | .load_store_register => |v| @bitCast(u32, v), | ||
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), | ||
| 377 | .load_literal => |v| @bitCast(u32, v), | ||
| 378 | .exception_generation => |v| @bitCast(u32, v), | ||
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), | ||
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), | ||
| 381 | .no_operation => |v| @bitCast(u32, v), | ||
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), | ||
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), | ||
| 384 | // TODO once packed structs work, this can be refactored | ||
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), | ||
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), | ||
| 387 | }; | ||
| 388 | } | ||
| 389 | |||
| 390 | fn moveWideImmediate( | ||
| 391 | opc: u2, | ||
| 392 | rd: Register, | ||
| 393 | imm16: u16, | ||
| 394 | shift: u6, | ||
| 395 | ) Instruction { | ||
| 396 | switch (rd.size()) { | ||
| 397 | 32 => { | ||
| 398 | assert(shift % 16 == 0 and shift <= 16); | ||
| 399 | return Instruction{ | ||
| 400 | .move_wide_immediate = .{ | ||
| 401 | .rd = rd.id(), | ||
| 402 | .imm16 = imm16, | ||
| 403 | .hw = @intCast(u2, shift / 16), | ||
| 404 | .opc = opc, | ||
| 405 | .sf = 0, | ||
| 406 | }, | ||
| 407 | }; | ||
| 408 | }, | ||
| 409 | 64 => { | ||
| 410 | assert(shift % 16 == 0 and shift <= 48); | ||
| 411 | return Instruction{ | ||
| 412 | .move_wide_immediate = .{ | ||
| 413 | .rd = rd.id(), | ||
| 414 | .imm16 = imm16, | ||
| 415 | .hw = @intCast(u2, shift / 16), | ||
| 416 | .opc = opc, | ||
| 417 | .sf = 1, | ||
| 418 | }, | ||
| 419 | }; | ||
| 420 | }, | ||
| 421 | else => unreachable, // unexpected register size | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { | ||
| 426 | assert(rd.size() == 64); | ||
| 427 | const imm21_u = @bitCast(u21, imm21); | ||
| 428 | return Instruction{ | ||
| 429 | .pc_relative_address = .{ | ||
| 430 | .rd = rd.id(), | ||
| 431 | .immlo = @truncate(u2, imm21_u), | ||
| 432 | .immhi = @truncate(u19, imm21_u >> 2), | ||
| 433 | .op = op, | ||
| 434 | }, | ||
| 435 | }; | ||
| 436 | } | ||
| 437 | |||
| 438 | /// Represents the offset operand of a load or store instruction. | ||
| 439 | /// Data can be loaded from memory with either an immediate offset | ||
| 440 | /// or an offset that is stored in some register. | ||
| 441 | pub const LoadStoreOffset = union(enum) { | ||
| 442 | Immediate: union(enum) { | ||
| 443 | PostIndex: i9, | ||
| 444 | PreIndex: i9, | ||
| 445 | Unsigned: u12, | ||
| 446 | }, | ||
| 447 | Register: struct { | ||
| 448 | rm: u5, | ||
| 449 | shift: union(enum) { | ||
| 450 | Uxtw: u2, | ||
| 451 | Lsl: u2, | ||
| 452 | Sxtw: u2, | ||
| 453 | Sxtx: u2, | ||
| 454 | }, | ||
| 455 | }, | ||
| 456 | |||
| 457 | pub const none = LoadStoreOffset{ | ||
| 458 | .Immediate = .{ .Unsigned = 0 }, | ||
| 459 | }; | ||
| 460 | |||
| 461 | pub fn toU12(self: LoadStoreOffset) u12 { | ||
| 462 | return switch (self) { | ||
| 463 | .Immediate => |imm_type| switch (imm_type) { | ||
| 464 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, | ||
| 465 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, | ||
| 466 | .Unsigned => |v| v, | ||
| 467 | }, | ||
| 468 | .Register => |r| switch (r.shift) { | ||
| 469 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, | ||
| 470 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, | ||
| 471 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, | ||
| 472 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, | ||
| 473 | }, | ||
| 474 | }; | ||
| 475 | } | ||
| 476 | |||
| 477 | pub fn imm(offset: u12) LoadStoreOffset { | ||
| 478 | return .{ | ||
| 479 | .Immediate = .{ .Unsigned = offset }, | ||
| 480 | }; | ||
| 481 | } | ||
| 482 | |||
| 483 | pub fn imm_post_index(offset: i9) LoadStoreOffset { | ||
| 484 | return .{ | ||
| 485 | .Immediate = .{ .PostIndex = offset }, | ||
| 486 | }; | ||
| 487 | } | ||
| 488 | |||
| 489 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { | ||
| 490 | return .{ | ||
| 491 | .Immediate = .{ .PreIndex = offset }, | ||
| 492 | }; | ||
| 493 | } | ||
| 494 | |||
| 495 | pub fn reg(rm: Register) LoadStoreOffset { | ||
| 496 | return .{ | ||
| 497 | .Register = .{ | ||
| 498 | .rm = rm.id(), | ||
| 499 | .shift = .{ | ||
| 500 | .Lsl = 0, | ||
| 501 | }, | ||
| 502 | }, | ||
| 503 | }; | ||
| 504 | } | ||
| 505 | |||
| 506 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { | ||
| 507 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | ||
| 508 | return .{ | ||
| 509 | .Register = .{ | ||
| 510 | .rm = rm.id(), | ||
| 511 | .shift = .{ | ||
| 512 | .Uxtw = shift, | ||
| 513 | }, | ||
| 514 | }, | ||
| 515 | }; | ||
| 516 | } | ||
| 517 | |||
| 518 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { | ||
| 519 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | ||
| 520 | return .{ | ||
| 521 | .Register = .{ | ||
| 522 | .rm = rm.id(), | ||
| 523 | .shift = .{ | ||
| 524 | .Lsl = shift, | ||
| 525 | }, | ||
| 526 | }, | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | |||
| 530 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { | ||
| 531 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | ||
| 532 | return .{ | ||
| 533 | .Register = .{ | ||
| 534 | .rm = rm.id(), | ||
| 535 | .shift = .{ | ||
| 536 | .Sxtw = shift, | ||
| 537 | }, | ||
| 538 | }, | ||
| 539 | }; | ||
| 540 | } | ||
| 541 | |||
| 542 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { | ||
| 543 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | ||
| 544 | return .{ | ||
| 545 | .Register = .{ | ||
| 546 | .rm = rm.id(), | ||
| 547 | .shift = .{ | ||
| 548 | .Sxtx = shift, | ||
| 549 | }, | ||
| 550 | }, | ||
| 551 | }; | ||
| 552 | } | ||
| 553 | }; | ||
| 554 | |||
| 555 | /// Which kind of load/store to perform | ||
| 556 | const LoadStoreVariant = enum { | ||
| 557 | /// 32-bit or 64-bit | ||
| 558 | str, | ||
| 559 | /// 16-bit, zero-extended | ||
| 560 | strh, | ||
| 561 | /// 8-bit, zero-extended | ||
| 562 | strb, | ||
| 563 | /// 32-bit or 64-bit | ||
| 564 | ldr, | ||
| 565 | /// 16-bit, zero-extended | ||
| 566 | ldrh, | ||
| 567 | /// 8-bit, zero-extended | ||
| 568 | ldrb, | ||
| 569 | }; | ||
| 570 | |||
| 571 | fn loadStoreRegister( | ||
| 572 | rt: Register, | ||
| 573 | rn: Register, | ||
| 574 | offset: LoadStoreOffset, | ||
| 575 | variant: LoadStoreVariant, | ||
| 576 | ) Instruction { | ||
| 577 | const off = offset.toU12(); | ||
| 578 | const op1: u2 = blk: { | ||
| 579 | switch (offset) { | ||
| 580 | .Immediate => |imm| switch (imm) { | ||
| 581 | .Unsigned => break :blk 0b01, | ||
| 582 | else => {}, | ||
| 583 | }, | ||
| 584 | else => {}, | ||
| 585 | } | ||
| 586 | break :blk 0b00; | ||
| 587 | }; | ||
| 588 | const opc: u2 = switch (variant) { | ||
| 589 | .ldr, .ldrh, .ldrb => 0b01, | ||
| 590 | .str, .strh, .strb => 0b00, | ||
| 591 | }; | ||
| 592 | return Instruction{ | ||
| 593 | .load_store_register = .{ | ||
| 594 | .rt = rt.id(), | ||
| 595 | .rn = rn.id(), | ||
| 596 | .offset = off, | ||
| 597 | .opc = opc, | ||
| 598 | .op1 = op1, | ||
| 599 | .v = 0, | ||
| 600 | .size = blk: { | ||
| 601 | switch (variant) { | ||
| 602 | .ldr, .str => switch (rt.size()) { | ||
| 603 | 32 => break :blk 0b10, | ||
| 604 | 64 => break :blk 0b11, | ||
| 605 | else => unreachable, // unexpected register size | ||
| 606 | }, | ||
| 607 | .ldrh, .strh => break :blk 0b01, | ||
| 608 | .ldrb, .strb => break :blk 0b00, | ||
| 609 | } | ||
| 610 | }, | ||
| 611 | }, | ||
| 612 | }; | ||
| 613 | } | ||
| 614 | |||
| 615 | fn loadStoreRegisterPair( | ||
| 616 | rt1: Register, | ||
| 617 | rt2: Register, | ||
| 618 | rn: Register, | ||
| 619 | offset: i9, | ||
| 620 | encoding: u2, | ||
| 621 | load: bool, | ||
| 622 | ) Instruction { | ||
| 623 | switch (rt1.size()) { | ||
| 624 | 32 => { | ||
| 625 | assert(-256 <= offset and offset <= 252); | ||
| 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); | ||
| 627 | return Instruction{ | ||
| 628 | .load_store_register_pair = .{ | ||
| 629 | .rt1 = rt1.id(), | ||
| 630 | .rn = rn.id(), | ||
| 631 | .rt2 = rt2.id(), | ||
| 632 | .imm7 = imm7, | ||
| 633 | .load = @boolToInt(load), | ||
| 634 | .encoding = encoding, | ||
| 635 | .opc = 0b00, | ||
| 636 | }, | ||
| 637 | }; | ||
| 638 | }, | ||
| 639 | 64 => { | ||
| 640 | assert(-512 <= offset and offset <= 504); | ||
| 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); | ||
| 642 | return Instruction{ | ||
| 643 | .load_store_register_pair = .{ | ||
| 644 | .rt1 = rt1.id(), | ||
| 645 | .rn = rn.id(), | ||
| 646 | .rt2 = rt2.id(), | ||
| 647 | .imm7 = imm7, | ||
| 648 | .load = @boolToInt(load), | ||
| 649 | .encoding = encoding, | ||
| 650 | .opc = 0b10, | ||
| 651 | }, | ||
| 652 | }; | ||
| 653 | }, | ||
| 654 | else => unreachable, // unexpected register size | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | ||
| 659 | switch (rt.size()) { | ||
| 660 | 32 => { | ||
| 661 | return Instruction{ | ||
| 662 | .load_literal = .{ | ||
| 663 | .rt = rt.id(), | ||
| 664 | .imm19 = imm19, | ||
| 665 | .opc = 0b00, | ||
| 666 | }, | ||
| 667 | }; | ||
| 668 | }, | ||
| 669 | 64 => { | ||
| 670 | return Instruction{ | ||
| 671 | .load_literal = .{ | ||
| 672 | .rt = rt.id(), | ||
| 673 | .imm19 = imm19, | ||
| 674 | .opc = 0b01, | ||
| 675 | }, | ||
| 676 | }; | ||
| 677 | }, | ||
| 678 | else => unreachable, // unexpected register size | ||
| 679 | } | ||
| 680 | } | ||
| 681 | |||
| 682 | fn exceptionGeneration( | ||
| 683 | opc: u3, | ||
| 684 | op2: u3, | ||
| 685 | ll: u2, | ||
| 686 | imm16: u16, | ||
| 687 | ) Instruction { | ||
| 688 | return Instruction{ | ||
| 689 | .exception_generation = .{ | ||
| 690 | .ll = ll, | ||
| 691 | .op2 = op2, | ||
| 692 | .imm16 = imm16, | ||
| 693 | .opc = opc, | ||
| 694 | }, | ||
| 695 | }; | ||
| 696 | } | ||
| 697 | |||
| 698 | fn unconditionalBranchRegister( | ||
| 699 | opc: u4, | ||
| 700 | op2: u5, | ||
| 701 | op3: u6, | ||
| 702 | rn: Register, | ||
| 703 | op4: u5, | ||
| 704 | ) Instruction { | ||
| 705 | assert(rn.size() == 64); | ||
| 706 | |||
| 707 | return Instruction{ | ||
| 708 | .unconditional_branch_register = .{ | ||
| 709 | .op4 = op4, | ||
| 710 | .rn = rn.id(), | ||
| 711 | .op3 = op3, | ||
| 712 | .op2 = op2, | ||
| 713 | .opc = opc, | ||
| 714 | }, | ||
| 715 | }; | ||
| 716 | } | ||
| 717 | |||
| 718 | fn unconditionalBranchImmediate( | ||
| 719 | op: u1, | ||
| 720 | offset: i28, | ||
| 721 | ) Instruction { | ||
| 722 | return Instruction{ | ||
| 723 | .unconditional_branch_immediate = .{ | ||
| 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), | ||
| 725 | .op = op, | ||
| 726 | }, | ||
| 727 | }; | ||
| 728 | } | ||
| 729 | |||
| 730 | fn logicalShiftedRegister( | ||
| 731 | opc: u2, | ||
| 732 | n: u1, | ||
| 733 | shift: Shift, | ||
| 734 | rd: Register, | ||
| 735 | rn: Register, | ||
| 736 | rm: Register, | ||
| 737 | ) Instruction { | ||
| 738 | switch (rd.size()) { | ||
| 739 | 32 => { | ||
| 740 | assert(shift.amount < 32); | ||
| 741 | return Instruction{ | ||
| 742 | .logical_shifted_register = .{ | ||
| 743 | .rd = rd.id(), | ||
| 744 | .rn = rn.id(), | ||
| 745 | .imm6 = shift.amount, | ||
| 746 | .rm = rm.id(), | ||
| 747 | .n = n, | ||
| 748 | .shift = @enumToInt(shift.shift), | ||
| 749 | .opc = opc, | ||
| 750 | .sf = 0b0, | ||
| 751 | }, | ||
| 752 | }; | ||
| 753 | }, | ||
| 754 | 64 => { | ||
| 755 | return Instruction{ | ||
| 756 | .logical_shifted_register = .{ | ||
| 757 | .rd = rd.id(), | ||
| 758 | .rn = rn.id(), | ||
| 759 | .imm6 = shift.amount, | ||
| 760 | .rm = rm.id(), | ||
| 761 | .n = n, | ||
| 762 | .shift = @enumToInt(shift.shift), | ||
| 763 | .opc = opc, | ||
| 764 | .sf = 0b1, | ||
| 765 | }, | ||
| 766 | }; | ||
| 767 | }, | ||
| 768 | else => unreachable, // unexpected register size | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | fn addSubtractImmediate( | ||
| 773 | op: u1, | ||
| 774 | s: u1, | ||
| 775 | rd: Register, | ||
| 776 | rn: Register, | ||
| 777 | imm12: u12, | ||
| 778 | shift: bool, | ||
| 779 | ) Instruction { | ||
| 780 | return Instruction{ | ||
| 781 | .add_subtract_immediate = .{ | ||
| 782 | .rd = rd.id(), | ||
| 783 | .rn = rn.id(), | ||
| 784 | .imm12 = imm12, | ||
| 785 | .sh = @boolToInt(shift), | ||
| 786 | .s = s, | ||
| 787 | .op = op, | ||
| 788 | .sf = switch (rd.size()) { | ||
| 789 | 32 => 0b0, | ||
| 790 | 64 => 0b1, | ||
| 791 | else => unreachable, // unexpected register size | ||
| 792 | }, | ||
| 793 | }, | ||
| 794 | }; | ||
| 795 | } | ||
| 796 | |||
| 797 | fn conditionalBranch( | ||
| 798 | o0: u1, | ||
| 799 | o1: u1, | ||
| 800 | cond: Condition, | ||
| 801 | offset: i21, | ||
| 802 | ) Instruction { | ||
| 803 | assert(offset & 0b11 == 0b00); | ||
| 804 | return Instruction{ | ||
| 805 | .conditional_branch = .{ | ||
| 806 | .cond = @enumToInt(cond), | ||
| 807 | .o0 = o0, | ||
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | ||
| 809 | .o1 = o1, | ||
| 810 | }, | ||
| 811 | }; | ||
| 812 | } | ||
| 813 | |||
| 814 | fn compareAndBranch( | ||
| 815 | op: u1, | ||
| 816 | rt: Register, | ||
| 817 | offset: i21, | ||
| 818 | ) Instruction { | ||
| 819 | assert(offset & 0b11 == 0b00); | ||
| 820 | return Instruction{ | ||
| 821 | .compare_and_branch = .{ | ||
| 822 | .rt = rt.id(), | ||
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | ||
| 824 | .op = op, | ||
| 825 | .sf = switch (rt.size()) { | ||
| 826 | 32 => 0b0, | ||
| 827 | 64 => 0b1, | ||
| 828 | else => unreachable, // unexpected register size | ||
| 829 | }, | ||
| 830 | }, | ||
| 831 | }; | ||
| 832 | } | ||
| 833 | |||
| 834 | // Helper functions for assembly syntax functions | ||
| 835 | |||
| 836 | // Move wide (immediate) | ||
| 837 | |||
| 838 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 839 | return moveWideImmediate(0b00, rd, imm16, shift); | ||
| 840 | } | ||
| 841 | |||
| 842 | pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 843 | return moveWideImmediate(0b10, rd, imm16, shift); | ||
| 844 | } | ||
| 845 | |||
| 846 | pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { | ||
| 847 | return moveWideImmediate(0b11, rd, imm16, shift); | ||
| 848 | } | ||
| 849 | |||
| 850 | // PC relative address | ||
| 851 | |||
| 852 | pub fn adr(rd: Register, imm21: i21) Instruction { | ||
| 853 | return pcRelativeAddress(rd, imm21, 0b0); | ||
| 854 | } | ||
| 855 | |||
| 856 | pub fn adrp(rd: Register, imm21: i21) Instruction { | ||
| 857 | return pcRelativeAddress(rd, imm21, 0b1); | ||
| 858 | } | ||
| 859 | |||
| 860 | // Load or store register | ||
| 861 | |||
| 862 | pub const LdrArgs = union(enum) { | ||
| 863 | register: struct { | ||
| 864 | rn: Register, | ||
| 865 | offset: LoadStoreOffset = LoadStoreOffset.none, | ||
| 866 | }, | ||
| 867 | literal: u19, | ||
| 868 | }; | ||
| 869 | |||
| 870 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | ||
| 871 | switch (args) { | ||
| 872 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), | ||
| 873 | .literal => |literal| return loadLiteral(rt, literal), | ||
| 874 | } | ||
| 875 | } | ||
| 876 | |||
| 877 | pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 878 | return loadStoreRegister(rt, rn, args.offset, .ldrh); | ||
| 879 | } | ||
| 880 | |||
| 881 | pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 882 | return loadStoreRegister(rt, rn, args.offset, .ldrb); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub const StrArgs = struct { | ||
| 886 | offset: LoadStoreOffset = LoadStoreOffset.none, | ||
| 887 | }; | ||
| 888 | |||
| 889 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 890 | return loadStoreRegister(rt, rn, args.offset, .str); | ||
| 891 | } | ||
| 892 | |||
| 893 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 894 | return loadStoreRegister(rt, rn, args.offset, .strh); | ||
| 895 | } | ||
| 896 | |||
| 897 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { | ||
| 898 | return loadStoreRegister(rt, rn, args.offset, .strb); | ||
| 899 | } | ||
| 900 | |||
| 901 | // Load or store pair of registers | ||
| 902 | |||
| 903 | pub const LoadStorePairOffset = struct { | ||
| 904 | encoding: enum(u2) { | ||
| 905 | PostIndex = 0b01, | ||
| 906 | Signed = 0b10, | ||
| 907 | PreIndex = 0b11, | ||
| 908 | }, | ||
| 909 | offset: i9, | ||
| 910 | |||
| 911 | pub fn none() LoadStorePairOffset { | ||
| 912 | return .{ .encoding = .Signed, .offset = 0 }; | ||
| 913 | } | ||
| 914 | |||
| 915 | pub fn post_index(imm: i9) LoadStorePairOffset { | ||
| 916 | return .{ .encoding = .PostIndex, .offset = imm }; | ||
| 917 | } | ||
| 918 | |||
| 919 | pub fn pre_index(imm: i9) LoadStorePairOffset { | ||
| 920 | return .{ .encoding = .PreIndex, .offset = imm }; | ||
| 921 | } | ||
| 922 | |||
| 923 | pub fn signed(imm: i9) LoadStorePairOffset { | ||
| 924 | return .{ .encoding = .Signed, .offset = imm }; | ||
| 925 | } | ||
| 926 | }; | ||
| 927 | |||
| 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | ||
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | ||
| 930 | } | ||
| 931 | |||
| 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | ||
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); | ||
| 934 | } | ||
| 935 | |||
| 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | ||
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | ||
| 938 | } | ||
| 939 | |||
| 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | ||
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); | ||
| 942 | } | ||
| 943 | |||
| 944 | // Exception generation | ||
| 945 | |||
| 946 | pub fn svc(imm16: u16) Instruction { | ||
| 947 | return exceptionGeneration(0b000, 0b000, 0b01, imm16); | ||
| 948 | } | ||
| 949 | |||
| 950 | pub fn hvc(imm16: u16) Instruction { | ||
| 951 | return exceptionGeneration(0b000, 0b000, 0b10, imm16); | ||
| 952 | } | ||
| 953 | |||
| 954 | pub fn smc(imm16: u16) Instruction { | ||
| 955 | return exceptionGeneration(0b000, 0b000, 0b11, imm16); | ||
| 956 | } | ||
| 957 | |||
| 958 | pub fn brk(imm16: u16) Instruction { | ||
| 959 | return exceptionGeneration(0b001, 0b000, 0b00, imm16); | ||
| 960 | } | ||
| 961 | |||
| 962 | pub fn hlt(imm16: u16) Instruction { | ||
| 963 | return exceptionGeneration(0b010, 0b000, 0b00, imm16); | ||
| 964 | } | ||
| 965 | |||
| 966 | // Unconditional branch (register) | ||
| 967 | |||
| 968 | pub fn br(rn: Register) Instruction { | ||
| 969 | return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); | ||
| 970 | } | ||
| 971 | |||
| 972 | pub fn blr(rn: Register) Instruction { | ||
| 973 | return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); | ||
| 974 | } | ||
| 975 | |||
| 976 | pub fn ret(rn: ?Register) Instruction { | ||
| 977 | return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); | ||
| 978 | } | ||
| 979 | |||
| 980 | // Unconditional branch (immediate) | ||
| 981 | |||
| 982 | pub fn b(offset: i28) Instruction { | ||
| 983 | return unconditionalBranchImmediate(0, offset); | ||
| 984 | } | ||
| 985 | |||
| 986 | pub fn bl(offset: i28) Instruction { | ||
| 987 | return unconditionalBranchImmediate(1, offset); | ||
| 988 | } | ||
| 989 | |||
| 990 | // Nop | ||
| 991 | |||
| 992 | pub fn nop() Instruction { | ||
| 993 | return Instruction{ .no_operation = .{} }; | ||
| 994 | } | ||
| 995 | |||
| 996 | // Logical (shifted register) | ||
| 997 | |||
| 998 | pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 999 | return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1003 | return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1007 | return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1011 | return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1015 | return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1019 | return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1023 | return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | ||
| 1027 | return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | // Add/subtract (immediate) | ||
| 1031 | |||
| 1032 | pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1033 | return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1037 | return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1041 | return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | ||
| 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | // Conditional branch | ||
| 1049 | |||
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { | ||
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | // Compare and branch | ||
| 1055 | |||
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { | ||
| 1057 | return compareAndBranch(0b0, rt, offset); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { | ||
| 1061 | return compareAndBranch(0b1, rt, offset); | ||
| 1062 | } | ||
| 1063 | }; | ||
| 1064 | |||
| 1065 | test { | ||
| 1066 | testing.refAllDecls(@This()); | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | test "serialize instructions" { | ||
| 1070 | const Testcase = struct { | ||
| 1071 | inst: Instruction, | ||
| 1072 | expected: u32, | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | const testcases = [_]Testcase{ | ||
| 1076 | .{ // orr x0, xzr, x1 | ||
| 1077 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), | ||
| 1078 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | ||
| 1079 | }, | ||
| 1080 | .{ // orn x0, xzr, x1 | ||
| 1081 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), | ||
| 1082 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | ||
| 1083 | }, | ||
| 1084 | .{ // movz x1, #4 | ||
| 1085 | .inst = Instruction.movz(.x1, 4, 0), | ||
| 1086 | .expected = 0b1_10_100101_00_0000000000000100_00001, | ||
| 1087 | }, | ||
| 1088 | .{ // movz x1, #4, lsl 16 | ||
| 1089 | .inst = Instruction.movz(.x1, 4, 16), | ||
| 1090 | .expected = 0b1_10_100101_01_0000000000000100_00001, | ||
| 1091 | }, | ||
| 1092 | .{ // movz x1, #4, lsl 32 | ||
| 1093 | .inst = Instruction.movz(.x1, 4, 32), | ||
| 1094 | .expected = 0b1_10_100101_10_0000000000000100_00001, | ||
| 1095 | }, | ||
| 1096 | .{ // movz x1, #4, lsl 48 | ||
| 1097 | .inst = Instruction.movz(.x1, 4, 48), | ||
| 1098 | .expected = 0b1_10_100101_11_0000000000000100_00001, | ||
| 1099 | }, | ||
| 1100 | .{ // movz w1, #4 | ||
| 1101 | .inst = Instruction.movz(.w1, 4, 0), | ||
| 1102 | .expected = 0b0_10_100101_00_0000000000000100_00001, | ||
| 1103 | }, | ||
| 1104 | .{ // movz w1, #4, lsl 16 | ||
| 1105 | .inst = Instruction.movz(.w1, 4, 16), | ||
| 1106 | .expected = 0b0_10_100101_01_0000000000000100_00001, | ||
| 1107 | }, | ||
| 1108 | .{ // svc #0 | ||
| 1109 | .inst = Instruction.svc(0), | ||
| 1110 | .expected = 0b1101_0100_000_0000000000000000_00001, | ||
| 1111 | }, | ||
| 1112 | .{ // svc #0x80 ; typical on Darwin | ||
| 1113 | .inst = Instruction.svc(0x80), | ||
| 1114 | .expected = 0b1101_0100_000_0000000010000000_00001, | ||
| 1115 | }, | ||
| 1116 | .{ // ret | ||
| 1117 | .inst = Instruction.ret(null), | ||
| 1118 | .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, | ||
| 1119 | }, | ||
| 1120 | .{ // bl #0x10 | ||
| 1121 | .inst = Instruction.bl(0x10), | ||
| 1122 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, | ||
| 1123 | }, | ||
| 1124 | .{ // ldr x2, [x1] | ||
| 1125 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }), | ||
| 1126 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | ||
| 1127 | }, | ||
| 1128 | .{ // ldr x2, [x1, #1]! | ||
| 1129 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }), | ||
| 1130 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | ||
| 1131 | }, | ||
| 1132 | .{ // ldr x2, [x1], #-1 | ||
| 1133 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }), | ||
| 1134 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | ||
| 1135 | }, | ||
| 1136 | .{ // ldr x2, [x1], (x3) | ||
| 1137 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }), | ||
| 1138 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, | ||
| 1139 | }, | ||
| 1140 | .{ // ldr x2, label | ||
| 1141 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), | ||
| 1142 | .expected = 0b01_011_0_00_0000000000000000001_00010, | ||
| 1143 | }, | ||
| 1144 | .{ // ldrh x7, [x4], #0xaa | ||
| 1145 | .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), | ||
| 1146 | .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, | ||
| 1147 | }, | ||
| 1148 | .{ // ldrb x9, [x15, #0xff]! | ||
| 1149 | .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), | ||
| 1150 | .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, | ||
| 1151 | }, | ||
| 1152 | .{ // str x2, [x1] | ||
| 1153 | .inst = Instruction.str(.x2, .x1, .{}), | ||
| 1154 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | ||
| 1155 | }, | ||
| 1156 | .{ // str x2, [x1], (x3) | ||
| 1157 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), | ||
| 1158 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | ||
| 1159 | }, | ||
| 1160 | .{ // strh w0, [x1] | ||
| 1161 | .inst = Instruction.strh(.w0, .x1, .{}), | ||
| 1162 | .expected = 0b01_111_0_01_00_000000000000_00001_00000, | ||
| 1163 | }, | ||
| 1164 | .{ // strb w8, [x9] | ||
| 1165 | .inst = Instruction.strb(.w8, .x9, .{}), | ||
| 1166 | .expected = 0b00_111_0_01_00_000000000000_01001_01000, | ||
| 1167 | }, | ||
| 1168 | .{ // adr x2, #0x8 | ||
| 1169 | .inst = Instruction.adr(.x2, 0x8), | ||
| 1170 | .expected = 0b0_00_10000_0000000000000000010_00010, | ||
| 1171 | }, | ||
| 1172 | .{ // adr x2, -#0x8 | ||
| 1173 | .inst = Instruction.adr(.x2, -0x8), | ||
| 1174 | .expected = 0b0_00_10000_1111111111111111110_00010, | ||
| 1175 | }, | ||
| 1176 | .{ // adrp x2, #0x8 | ||
| 1177 | .inst = Instruction.adrp(.x2, 0x8), | ||
| 1178 | .expected = 0b1_00_10000_0000000000000000010_00010, | ||
| 1179 | }, | ||
| 1180 | .{ // adrp x2, -#0x8 | ||
| 1181 | .inst = Instruction.adrp(.x2, -0x8), | ||
| 1182 | .expected = 0b1_00_10000_1111111111111111110_00010, | ||
| 1183 | }, | ||
| 1184 | .{ // stp x1, x2, [sp, #8] | ||
| 1185 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | ||
| 1186 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, | ||
| 1187 | }, | ||
| 1188 | .{ // ldp x1, x2, [sp, #8] | ||
| 1189 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | ||
| 1190 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, | ||
| 1191 | }, | ||
| 1192 | .{ // stp x1, x2, [sp, #-16]! | ||
| 1193 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), | ||
| 1194 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, | ||
| 1195 | }, | ||
| 1196 | .{ // ldp x1, x2, [sp], #16 | ||
| 1197 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), | ||
| 1198 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, | ||
| 1199 | }, | ||
| 1200 | .{ // and x0, x4, x2 | ||
| 1201 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), | ||
| 1202 | .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, | ||
| 1203 | }, | ||
| 1204 | .{ // and x0, x4, x2, lsl #0x8 | ||
| 1205 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), | ||
| 1206 | .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, | ||
| 1207 | }, | ||
| 1208 | .{ // add x0, x10, #10 | ||
| 1209 | .inst = Instruction.add(.x0, .x10, 10, false), | ||
| 1210 | .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, | ||
| 1211 | }, | ||
| 1212 | .{ // subs x0, x5, #11, lsl #12 | ||
| 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), | ||
| 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, | ||
| 1215 | }, | ||
| 1216 | .{ // b.hi #-4 | ||
| 1217 | .inst = Instruction.bCond(.hi, -4), | ||
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, | ||
| 1219 | }, | ||
| 1220 | .{ // cbz x10, #40 | ||
| 1221 | .inst = Instruction.cbz(.x10, 40), | ||
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, | ||
| 1223 | }, | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | for (testcases) |case| { | ||
| 1227 | const actual = case.inst.toU32(); | ||
| 1228 | try testing.expectEqual(case.expected, actual); | ||
| 1229 | } | ||
| 1230 | } | ||
src/codegen/arm.zig deleted-1408| ... | @@ -1,1408 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | /// The condition field specifies the flags necessary for an | ||
| 6 | /// Instruction to be executed | ||
| 7 | pub const Condition = enum(u4) { | ||
| 8 | /// equal | ||
| 9 | eq, | ||
| 10 | /// not equal | ||
| 11 | ne, | ||
| 12 | /// unsigned higher or same | ||
| 13 | cs, | ||
| 14 | /// unsigned lower | ||
| 15 | cc, | ||
| 16 | /// negative | ||
| 17 | mi, | ||
| 18 | /// positive or zero | ||
| 19 | pl, | ||
| 20 | /// overflow | ||
| 21 | vs, | ||
| 22 | /// no overflow | ||
| 23 | vc, | ||
| 24 | /// unsigned higer | ||
| 25 | hi, | ||
| 26 | /// unsigned lower or same | ||
| 27 | ls, | ||
| 28 | /// greater or equal | ||
| 29 | ge, | ||
| 30 | /// less than | ||
| 31 | lt, | ||
| 32 | /// greater than | ||
| 33 | gt, | ||
| 34 | /// less than or equal | ||
| 35 | le, | ||
| 36 | /// always | ||
| 37 | al, | ||
| 38 | |||
| 39 | /// Converts a std.math.CompareOperator into a condition flag, | ||
| 40 | /// i.e. returns the condition that is true iff the result of the | ||
| 41 | /// comparison is true. Assumes signed comparison | ||
| 42 | pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { | ||
| 43 | return switch (op) { | ||
| 44 | .gte => .ge, | ||
| 45 | .gt => .gt, | ||
| 46 | .neq => .ne, | ||
| 47 | .lt => .lt, | ||
| 48 | .lte => .le, | ||
| 49 | .eq => .eq, | ||
| 50 | }; | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Converts a std.math.CompareOperator into a condition flag, | ||
| 54 | /// i.e. returns the condition that is true iff the result of the | ||
| 55 | /// comparison is true. Assumes unsigned comparison | ||
| 56 | pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { | ||
| 57 | return switch (op) { | ||
| 58 | .gte => .cs, | ||
| 59 | .gt => .hi, | ||
| 60 | .neq => .ne, | ||
| 61 | .lt => .cc, | ||
| 62 | .lte => .ls, | ||
| 63 | .eq => .eq, | ||
| 64 | }; | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Returns the condition which is true iff the given condition is | ||
| 68 | /// false (if such a condition exists) | ||
| 69 | pub fn negate(cond: Condition) Condition { | ||
| 70 | return switch (cond) { | ||
| 71 | .eq => .ne, | ||
| 72 | .ne => .eq, | ||
| 73 | .cs => .cc, | ||
| 74 | .cc => .cs, | ||
| 75 | .mi => .pl, | ||
| 76 | .pl => .mi, | ||
| 77 | .vs => .vc, | ||
| 78 | .vc => .vs, | ||
| 79 | .hi => .ls, | ||
| 80 | .ls => .hi, | ||
| 81 | .ge => .lt, | ||
| 82 | .lt => .ge, | ||
| 83 | .gt => .le, | ||
| 84 | .le => .gt, | ||
| 85 | .al => unreachable, | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | test "condition from CompareOperator" { | ||
| 91 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); | ||
| 92 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); | ||
| 93 | |||
| 94 | try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); | ||
| 95 | try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); | ||
| 96 | |||
| 97 | try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); | ||
| 98 | try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); | ||
| 99 | } | ||
| 100 | |||
| 101 | test "negate condition" { | ||
| 102 | try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); | ||
| 103 | try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Represents a register in the ARM instruction set architecture | ||
| 107 | pub const Register = enum(u5) { | ||
| 108 | r0, | ||
| 109 | r1, | ||
| 110 | r2, | ||
| 111 | r3, | ||
| 112 | r4, | ||
| 113 | r5, | ||
| 114 | r6, | ||
| 115 | r7, | ||
| 116 | r8, | ||
| 117 | r9, | ||
| 118 | r10, | ||
| 119 | r11, | ||
| 120 | r12, | ||
| 121 | r13, | ||
| 122 | r14, | ||
| 123 | r15, | ||
| 124 | |||
| 125 | /// Argument / result / scratch register 1 | ||
| 126 | a1, | ||
| 127 | /// Argument / result / scratch register 2 | ||
| 128 | a2, | ||
| 129 | /// Argument / scratch register 3 | ||
| 130 | a3, | ||
| 131 | /// Argument / scratch register 4 | ||
| 132 | a4, | ||
| 133 | /// Variable-register 1 | ||
| 134 | v1, | ||
| 135 | /// Variable-register 2 | ||
| 136 | v2, | ||
| 137 | /// Variable-register 3 | ||
| 138 | v3, | ||
| 139 | /// Variable-register 4 | ||
| 140 | v4, | ||
| 141 | /// Variable-register 5 | ||
| 142 | v5, | ||
| 143 | /// Platform register | ||
| 144 | v6, | ||
| 145 | /// Variable-register 7 | ||
| 146 | v7, | ||
| 147 | /// Frame pointer or Variable-register 8 | ||
| 148 | fp, | ||
| 149 | /// Intra-Procedure-call scratch register | ||
| 150 | ip, | ||
| 151 | /// Stack pointer | ||
| 152 | sp, | ||
| 153 | /// Link register | ||
| 154 | lr, | ||
| 155 | /// Program counter | ||
| 156 | pc, | ||
| 157 | |||
| 158 | /// Returns the unique 4-bit ID of this register which is used in | ||
| 159 | /// the machine code | ||
| 160 | pub fn id(self: Register) u4 { | ||
| 161 | return @truncate(u4, @enumToInt(self)); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Returns the index into `callee_preserved_regs`. | ||
| 165 | pub fn allocIndex(self: Register) ?u4 { | ||
| 166 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 167 | if (self.id() == cpreg.id()) return i; | ||
| 168 | } | ||
| 169 | return null; | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 173 | return @as(u8, self.id()) + DW.OP.reg0; | ||
| 174 | } | ||
| 175 | }; | ||
| 176 | |||
| 177 | test "Register.id" { | ||
| 178 | try testing.expectEqual(@as(u4, 15), Register.r15.id()); | ||
| 179 | try testing.expectEqual(@as(u4, 15), Register.pc.id()); | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Program status registers containing flags, mode bits and other | ||
| 183 | /// vital information | ||
| 184 | pub const Psr = enum { | ||
| 185 | cpsr, | ||
| 186 | spsr, | ||
| 187 | }; | ||
| 188 | |||
| 189 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | ||
| 190 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 191 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ||
| 192 | |||
| 193 | /// Represents an instruction in the ARM instruction set architecture | ||
| 194 | pub const Instruction = union(enum) { | ||
| 195 | data_processing: packed struct { | ||
| 196 | // Note to self: The order of the fields top-to-bottom is | ||
| 197 | // right-to-left in the actual 32-bit int representation | ||
| 198 | op2: u12, | ||
| 199 | rd: u4, | ||
| 200 | rn: u4, | ||
| 201 | s: u1, | ||
| 202 | opcode: u4, | ||
| 203 | i: u1, | ||
| 204 | fixed: u2 = 0b00, | ||
| 205 | cond: u4, | ||
| 206 | }, | ||
| 207 | multiply: packed struct { | ||
| 208 | rn: u4, | ||
| 209 | fixed_1: u4 = 0b1001, | ||
| 210 | rm: u4, | ||
| 211 | ra: u4, | ||
| 212 | rd: u4, | ||
| 213 | set_cond: u1, | ||
| 214 | accumulate: u1, | ||
| 215 | fixed_2: u6 = 0b000000, | ||
| 216 | cond: u4, | ||
| 217 | }, | ||
| 218 | multiply_long: packed struct { | ||
| 219 | rn: u4, | ||
| 220 | fixed_1: u4 = 0b1001, | ||
| 221 | rm: u4, | ||
| 222 | rdlo: u4, | ||
| 223 | rdhi: u4, | ||
| 224 | set_cond: u1, | ||
| 225 | accumulate: u1, | ||
| 226 | unsigned: u1, | ||
| 227 | fixed_2: u5 = 0b00001, | ||
| 228 | cond: u4, | ||
| 229 | }, | ||
| 230 | integer_saturating_arithmetic: packed struct { | ||
| 231 | rm: u4, | ||
| 232 | fixed_1: u8 = 0b0000_0101, | ||
| 233 | rd: u4, | ||
| 234 | rn: u4, | ||
| 235 | fixed_2: u1 = 0b0, | ||
| 236 | opc: u2, | ||
| 237 | fixed_3: u5 = 0b00010, | ||
| 238 | cond: u4, | ||
| 239 | }, | ||
| 240 | single_data_transfer: packed struct { | ||
| 241 | offset: u12, | ||
| 242 | rd: u4, | ||
| 243 | rn: u4, | ||
| 244 | load_store: u1, | ||
| 245 | write_back: u1, | ||
| 246 | byte_word: u1, | ||
| 247 | up_down: u1, | ||
| 248 | pre_post: u1, | ||
| 249 | imm: u1, | ||
| 250 | fixed: u2 = 0b01, | ||
| 251 | cond: u4, | ||
| 252 | }, | ||
| 253 | extra_load_store: packed struct { | ||
| 254 | imm4l: u4, | ||
| 255 | fixed_1: u1 = 0b1, | ||
| 256 | op2: u2, | ||
| 257 | fixed_2: u1 = 0b1, | ||
| 258 | imm4h: u4, | ||
| 259 | rt: u4, | ||
| 260 | rn: u4, | ||
| 261 | o1: u1, | ||
| 262 | write_back: u1, | ||
| 263 | imm: u1, | ||
| 264 | up_down: u1, | ||
| 265 | pre_index: u1, | ||
| 266 | fixed_3: u3 = 0b000, | ||
| 267 | cond: u4, | ||
| 268 | }, | ||
| 269 | block_data_transfer: packed struct { | ||
| 270 | register_list: u16, | ||
| 271 | rn: u4, | ||
| 272 | load_store: u1, | ||
| 273 | write_back: u1, | ||
| 274 | psr_or_user: u1, | ||
| 275 | up_down: u1, | ||
| 276 | pre_post: u1, | ||
| 277 | fixed: u3 = 0b100, | ||
| 278 | cond: u4, | ||
| 279 | }, | ||
| 280 | branch: packed struct { | ||
| 281 | offset: u24, | ||
| 282 | link: u1, | ||
| 283 | fixed: u3 = 0b101, | ||
| 284 | cond: u4, | ||
| 285 | }, | ||
| 286 | branch_exchange: packed struct { | ||
| 287 | rn: u4, | ||
| 288 | fixed_1: u1 = 0b1, | ||
| 289 | link: u1, | ||
| 290 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | ||
| 291 | cond: u4, | ||
| 292 | }, | ||
| 293 | supervisor_call: packed struct { | ||
| 294 | comment: u24, | ||
| 295 | fixed: u4 = 0b1111, | ||
| 296 | cond: u4, | ||
| 297 | }, | ||
| 298 | breakpoint: packed struct { | ||
| 299 | imm4: u4, | ||
| 300 | fixed_1: u4 = 0b0111, | ||
| 301 | imm12: u12, | ||
| 302 | fixed_2_and_cond: u12 = 0b1110_0001_0010, | ||
| 303 | }, | ||
| 304 | |||
| 305 | /// Represents the possible operations which can be performed by a | ||
| 306 | /// Data Processing instruction | ||
| 307 | const Opcode = enum(u4) { | ||
| 308 | // Rd := Op1 AND Op2 | ||
| 309 | @"and", | ||
| 310 | // Rd := Op1 EOR Op2 | ||
| 311 | eor, | ||
| 312 | // Rd := Op1 - Op2 | ||
| 313 | sub, | ||
| 314 | // Rd := Op2 - Op1 | ||
| 315 | rsb, | ||
| 316 | // Rd := Op1 + Op2 | ||
| 317 | add, | ||
| 318 | // Rd := Op1 + Op2 + C | ||
| 319 | adc, | ||
| 320 | // Rd := Op1 - Op2 + C - 1 | ||
| 321 | sbc, | ||
| 322 | // Rd := Op2 - Op1 + C - 1 | ||
| 323 | rsc, | ||
| 324 | // set condition codes on Op1 AND Op2 | ||
| 325 | tst, | ||
| 326 | // set condition codes on Op1 EOR Op2 | ||
| 327 | teq, | ||
| 328 | // set condition codes on Op1 - Op2 | ||
| 329 | cmp, | ||
| 330 | // set condition codes on Op1 + Op2 | ||
| 331 | cmn, | ||
| 332 | // Rd := Op1 OR Op2 | ||
| 333 | orr, | ||
| 334 | // Rd := Op2 | ||
| 335 | mov, | ||
| 336 | // Rd := Op1 AND NOT Op2 | ||
| 337 | bic, | ||
| 338 | // Rd := NOT Op2 | ||
| 339 | mvn, | ||
| 340 | }; | ||
| 341 | |||
| 342 | /// Represents the second operand to a data processing instruction | ||
| 343 | /// which can either be content from a register or an immediate | ||
| 344 | /// value | ||
| 345 | pub const Operand = union(enum) { | ||
| 346 | Register: packed struct { | ||
| 347 | rm: u4, | ||
| 348 | shift: u8, | ||
| 349 | }, | ||
| 350 | Immediate: packed struct { | ||
| 351 | imm: u8, | ||
| 352 | rotate: u4, | ||
| 353 | }, | ||
| 354 | |||
| 355 | /// Represents multiple ways a register can be shifted. A | ||
| 356 | /// register can be shifted by a specific immediate value or | ||
| 357 | /// by the contents of another register | ||
| 358 | pub const Shift = union(enum) { | ||
| 359 | Immediate: packed struct { | ||
| 360 | fixed: u1 = 0b0, | ||
| 361 | typ: u2, | ||
| 362 | amount: u5, | ||
| 363 | }, | ||
| 364 | Register: packed struct { | ||
| 365 | fixed_1: u1 = 0b1, | ||
| 366 | typ: u2, | ||
| 367 | fixed_2: u1 = 0b0, | ||
| 368 | rs: u4, | ||
| 369 | }, | ||
| 370 | |||
| 371 | pub const Type = enum(u2) { | ||
| 372 | logical_left, | ||
| 373 | logical_right, | ||
| 374 | arithmetic_right, | ||
| 375 | rotate_right, | ||
| 376 | }; | ||
| 377 | |||
| 378 | pub const none = Shift{ | ||
| 379 | .Immediate = .{ | ||
| 380 | .amount = 0, | ||
| 381 | .typ = 0, | ||
| 382 | }, | ||
| 383 | }; | ||
| 384 | |||
| 385 | pub fn toU8(self: Shift) u8 { | ||
| 386 | return switch (self) { | ||
| 387 | .Register => |v| @bitCast(u8, v), | ||
| 388 | .Immediate => |v| @bitCast(u8, v), | ||
| 389 | }; | ||
| 390 | } | ||
| 391 | |||
| 392 | pub fn reg(rs: Register, typ: Type) Shift { | ||
| 393 | return Shift{ | ||
| 394 | .Register = .{ | ||
| 395 | .rs = rs.id(), | ||
| 396 | .typ = @enumToInt(typ), | ||
| 397 | }, | ||
| 398 | }; | ||
| 399 | } | ||
| 400 | |||
| 401 | pub fn imm(amount: u5, typ: Type) Shift { | ||
| 402 | return Shift{ | ||
| 403 | .Immediate = .{ | ||
| 404 | .amount = amount, | ||
| 405 | .typ = @enumToInt(typ), | ||
| 406 | }, | ||
| 407 | }; | ||
| 408 | } | ||
| 409 | }; | ||
| 410 | |||
| 411 | pub fn toU12(self: Operand) u12 { | ||
| 412 | return switch (self) { | ||
| 413 | .Register => |v| @bitCast(u12, v), | ||
| 414 | .Immediate => |v| @bitCast(u12, v), | ||
| 415 | }; | ||
| 416 | } | ||
| 417 | |||
| 418 | pub fn reg(rm: Register, shift: Shift) Operand { | ||
| 419 | return Operand{ | ||
| 420 | .Register = .{ | ||
| 421 | .rm = rm.id(), | ||
| 422 | .shift = shift.toU8(), | ||
| 423 | }, | ||
| 424 | }; | ||
| 425 | } | ||
| 426 | |||
| 427 | pub fn imm(immediate: u8, rotate: u4) Operand { | ||
| 428 | return Operand{ | ||
| 429 | .Immediate = .{ | ||
| 430 | .imm = immediate, | ||
| 431 | .rotate = rotate, | ||
| 432 | }, | ||
| 433 | }; | ||
| 434 | } | ||
| 435 | |||
| 436 | /// Tries to convert an unsigned 32 bit integer into an | ||
| 437 | /// immediate operand using rotation. Returns null when there | ||
| 438 | /// is no conversion | ||
| 439 | pub fn fromU32(x: u32) ?Operand { | ||
| 440 | const masks = comptime blk: { | ||
| 441 | const base_mask: u32 = std.math.maxInt(u8); | ||
| 442 | var result = [_]u32{0} ** 16; | ||
| 443 | for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i); | ||
| 444 | break :blk result; | ||
| 445 | }; | ||
| 446 | |||
| 447 | return for (masks) |mask, i| { | ||
| 448 | if (x & mask == x) { | ||
| 449 | break Operand{ | ||
| 450 | .Immediate = .{ | ||
| 451 | .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)), | ||
| 452 | .rotate = @intCast(u4, i), | ||
| 453 | }, | ||
| 454 | }; | ||
| 455 | } | ||
| 456 | } else null; | ||
| 457 | } | ||
| 458 | }; | ||
| 459 | |||
| 460 | /// Represents the offset operand of a load or store | ||
| 461 | /// instruction. Data can be loaded from memory with either an | ||
| 462 | /// immediate offset or an offset that is stored in some register. | ||
| 463 | pub const Offset = union(enum) { | ||
| 464 | Immediate: u12, | ||
| 465 | Register: packed struct { | ||
| 466 | rm: u4, | ||
| 467 | shift: u8, | ||
| 468 | }, | ||
| 469 | |||
| 470 | pub const none = Offset{ | ||
| 471 | .Immediate = 0, | ||
| 472 | }; | ||
| 473 | |||
| 474 | pub fn toU12(self: Offset) u12 { | ||
| 475 | return switch (self) { | ||
| 476 | .Register => |v| @bitCast(u12, v), | ||
| 477 | .Immediate => |v| v, | ||
| 478 | }; | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn reg(rm: Register, shift: u8) Offset { | ||
| 482 | return Offset{ | ||
| 483 | .Register = .{ | ||
| 484 | .rm = rm.id(), | ||
| 485 | .shift = shift, | ||
| 486 | }, | ||
| 487 | }; | ||
| 488 | } | ||
| 489 | |||
| 490 | pub fn imm(immediate: u12) Offset { | ||
| 491 | return Offset{ | ||
| 492 | .Immediate = immediate, | ||
| 493 | }; | ||
| 494 | } | ||
| 495 | }; | ||
| 496 | |||
| 497 | /// Represents the offset operand of an extra load or store | ||
| 498 | /// instruction. | ||
| 499 | pub const ExtraLoadStoreOffset = union(enum) { | ||
| 500 | immediate: u8, | ||
| 501 | register: u4, | ||
| 502 | |||
| 503 | pub const none = ExtraLoadStoreOffset{ | ||
| 504 | .immediate = 0, | ||
| 505 | }; | ||
| 506 | |||
| 507 | pub fn reg(register: Register) ExtraLoadStoreOffset { | ||
| 508 | return ExtraLoadStoreOffset{ | ||
| 509 | .register = register.id(), | ||
| 510 | }; | ||
| 511 | } | ||
| 512 | |||
| 513 | pub fn imm(immediate: u8) ExtraLoadStoreOffset { | ||
| 514 | return ExtraLoadStoreOffset{ | ||
| 515 | .immediate = immediate, | ||
| 516 | }; | ||
| 517 | } | ||
| 518 | }; | ||
| 519 | |||
| 520 | /// Represents the register list operand to a block data transfer | ||
| 521 | /// instruction | ||
| 522 | pub const RegisterList = packed struct { | ||
| 523 | r0: bool = false, | ||
| 524 | r1: bool = false, | ||
| 525 | r2: bool = false, | ||
| 526 | r3: bool = false, | ||
| 527 | r4: bool = false, | ||
| 528 | r5: bool = false, | ||
| 529 | r6: bool = false, | ||
| 530 | r7: bool = false, | ||
| 531 | r8: bool = false, | ||
| 532 | r9: bool = false, | ||
| 533 | r10: bool = false, | ||
| 534 | r11: bool = false, | ||
| 535 | r12: bool = false, | ||
| 536 | r13: bool = false, | ||
| 537 | r14: bool = false, | ||
| 538 | r15: bool = false, | ||
| 539 | }; | ||
| 540 | |||
| 541 | pub fn toU32(self: Instruction) u32 { | ||
| 542 | return switch (self) { | ||
| 543 | .data_processing => |v| @bitCast(u32, v), | ||
| 544 | .multiply => |v| @bitCast(u32, v), | ||
| 545 | .multiply_long => |v| @bitCast(u32, v), | ||
| 546 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), | ||
| 547 | .single_data_transfer => |v| @bitCast(u32, v), | ||
| 548 | .extra_load_store => |v| @bitCast(u32, v), | ||
| 549 | .block_data_transfer => |v| @bitCast(u32, v), | ||
| 550 | .branch => |v| @bitCast(u32, v), | ||
| 551 | .branch_exchange => |v| @bitCast(u32, v), | ||
| 552 | .supervisor_call => |v| @bitCast(u32, v), | ||
| 553 | .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), | ||
| 554 | }; | ||
| 555 | } | ||
| 556 | |||
| 557 | // Helper functions for the "real" functions below | ||
| 558 | |||
| 559 | fn dataProcessing( | ||
| 560 | cond: Condition, | ||
| 561 | opcode: Opcode, | ||
| 562 | s: u1, | ||
| 563 | rd: Register, | ||
| 564 | rn: Register, | ||
| 565 | op2: Operand, | ||
| 566 | ) Instruction { | ||
| 567 | return Instruction{ | ||
| 568 | .data_processing = .{ | ||
| 569 | .cond = @enumToInt(cond), | ||
| 570 | .i = @boolToInt(op2 == .Immediate), | ||
| 571 | .opcode = @enumToInt(opcode), | ||
| 572 | .s = s, | ||
| 573 | .rn = rn.id(), | ||
| 574 | .rd = rd.id(), | ||
| 575 | .op2 = op2.toU12(), | ||
| 576 | }, | ||
| 577 | }; | ||
| 578 | } | ||
| 579 | |||
| 580 | fn specialMov( | ||
| 581 | cond: Condition, | ||
| 582 | rd: Register, | ||
| 583 | imm: u16, | ||
| 584 | top: bool, | ||
| 585 | ) Instruction { | ||
| 586 | return Instruction{ | ||
| 587 | .data_processing = .{ | ||
| 588 | .cond = @enumToInt(cond), | ||
| 589 | .i = 1, | ||
| 590 | .opcode = if (top) 0b1010 else 0b1000, | ||
| 591 | .s = 0, | ||
| 592 | .rn = @truncate(u4, imm >> 12), | ||
| 593 | .rd = rd.id(), | ||
| 594 | .op2 = @truncate(u12, imm), | ||
| 595 | }, | ||
| 596 | }; | ||
| 597 | } | ||
| 598 | |||
| 599 | fn multiply( | ||
| 600 | cond: Condition, | ||
| 601 | set_cond: u1, | ||
| 602 | rd: Register, | ||
| 603 | rn: Register, | ||
| 604 | rm: Register, | ||
| 605 | ra: ?Register, | ||
| 606 | ) Instruction { | ||
| 607 | return Instruction{ | ||
| 608 | .multiply = .{ | ||
| 609 | .cond = @enumToInt(cond), | ||
| 610 | .accumulate = @boolToInt(ra != null), | ||
| 611 | .set_cond = set_cond, | ||
| 612 | .rd = rd.id(), | ||
| 613 | .rn = rn.id(), | ||
| 614 | .ra = if (ra) |reg| reg.id() else 0b0000, | ||
| 615 | .rm = rm.id(), | ||
| 616 | }, | ||
| 617 | }; | ||
| 618 | } | ||
| 619 | |||
| 620 | fn multiplyLong( | ||
| 621 | cond: Condition, | ||
| 622 | signed: u1, | ||
| 623 | accumulate: u1, | ||
| 624 | set_cond: u1, | ||
| 625 | rdhi: Register, | ||
| 626 | rdlo: Register, | ||
| 627 | rm: Register, | ||
| 628 | rn: Register, | ||
| 629 | ) Instruction { | ||
| 630 | return Instruction{ | ||
| 631 | .multiply_long = .{ | ||
| 632 | .cond = @enumToInt(cond), | ||
| 633 | .unsigned = signed, | ||
| 634 | .accumulate = accumulate, | ||
| 635 | .set_cond = set_cond, | ||
| 636 | .rdlo = rdlo.id(), | ||
| 637 | .rdhi = rdhi.id(), | ||
| 638 | .rn = rn.id(), | ||
| 639 | .rm = rm.id(), | ||
| 640 | }, | ||
| 641 | }; | ||
| 642 | } | ||
| 643 | |||
| 644 | fn integerSaturationArithmetic( | ||
| 645 | cond: Condition, | ||
| 646 | rd: Register, | ||
| 647 | rm: Register, | ||
| 648 | rn: Register, | ||
| 649 | opc: u2, | ||
| 650 | ) Instruction { | ||
| 651 | return Instruction{ | ||
| 652 | .integer_saturating_arithmetic = .{ | ||
| 653 | .rm = rm.id(), | ||
| 654 | .rd = rd.id(), | ||
| 655 | .rn = rn.id(), | ||
| 656 | .opc = opc, | ||
| 657 | .cond = @enumToInt(cond), | ||
| 658 | }, | ||
| 659 | }; | ||
| 660 | } | ||
| 661 | |||
| 662 | fn singleDataTransfer( | ||
| 663 | cond: Condition, | ||
| 664 | rd: Register, | ||
| 665 | rn: Register, | ||
| 666 | offset: Offset, | ||
| 667 | pre_index: bool, | ||
| 668 | positive: bool, | ||
| 669 | byte_word: u1, | ||
| 670 | write_back: bool, | ||
| 671 | load_store: u1, | ||
| 672 | ) Instruction { | ||
| 673 | return Instruction{ | ||
| 674 | .single_data_transfer = .{ | ||
| 675 | .cond = @enumToInt(cond), | ||
| 676 | .rn = rn.id(), | ||
| 677 | .rd = rd.id(), | ||
| 678 | .offset = offset.toU12(), | ||
| 679 | .load_store = load_store, | ||
| 680 | .write_back = @boolToInt(write_back), | ||
| 681 | .byte_word = byte_word, | ||
| 682 | .up_down = @boolToInt(positive), | ||
| 683 | .pre_post = @boolToInt(pre_index), | ||
| 684 | .imm = @boolToInt(offset != .Immediate), | ||
| 685 | }, | ||
| 686 | }; | ||
| 687 | } | ||
| 688 | |||
| 689 | fn extraLoadStore( | ||
| 690 | cond: Condition, | ||
| 691 | pre_index: bool, | ||
| 692 | positive: bool, | ||
| 693 | write_back: bool, | ||
| 694 | o1: u1, | ||
| 695 | op2: u2, | ||
| 696 | rn: Register, | ||
| 697 | rt: Register, | ||
| 698 | offset: ExtraLoadStoreOffset, | ||
| 699 | ) Instruction { | ||
| 700 | const imm4l: u4 = switch (offset) { | ||
| 701 | .immediate => |imm| @truncate(u4, imm), | ||
| 702 | .register => |reg| reg, | ||
| 703 | }; | ||
| 704 | const imm4h: u4 = switch (offset) { | ||
| 705 | .immediate => |imm| @truncate(u4, imm >> 4), | ||
| 706 | .register => 0b0000, | ||
| 707 | }; | ||
| 708 | |||
| 709 | return Instruction{ | ||
| 710 | .extra_load_store = .{ | ||
| 711 | .imm4l = imm4l, | ||
| 712 | .op2 = op2, | ||
| 713 | .imm4h = imm4h, | ||
| 714 | .rt = rt.id(), | ||
| 715 | .rn = rn.id(), | ||
| 716 | .o1 = o1, | ||
| 717 | .write_back = @boolToInt(write_back), | ||
| 718 | .imm = @boolToInt(offset == .immediate), | ||
| 719 | .up_down = @boolToInt(positive), | ||
| 720 | .pre_index = @boolToInt(pre_index), | ||
| 721 | .cond = @enumToInt(cond), | ||
| 722 | }, | ||
| 723 | }; | ||
| 724 | } | ||
| 725 | |||
| 726 | fn blockDataTransfer( | ||
| 727 | cond: Condition, | ||
| 728 | rn: Register, | ||
| 729 | reg_list: RegisterList, | ||
| 730 | pre_post: u1, | ||
| 731 | up_down: u1, | ||
| 732 | psr_or_user: u1, | ||
| 733 | write_back: bool, | ||
| 734 | load_store: u1, | ||
| 735 | ) Instruction { | ||
| 736 | return Instruction{ | ||
| 737 | .block_data_transfer = .{ | ||
| 738 | .register_list = @bitCast(u16, reg_list), | ||
| 739 | .rn = rn.id(), | ||
| 740 | .load_store = load_store, | ||
| 741 | .write_back = @boolToInt(write_back), | ||
| 742 | .psr_or_user = psr_or_user, | ||
| 743 | .up_down = up_down, | ||
| 744 | .pre_post = pre_post, | ||
| 745 | .cond = @enumToInt(cond), | ||
| 746 | }, | ||
| 747 | }; | ||
| 748 | } | ||
| 749 | |||
| 750 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | ||
| 751 | return Instruction{ | ||
| 752 | .branch = .{ | ||
| 753 | .cond = @enumToInt(cond), | ||
| 754 | .link = link, | ||
| 755 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), | ||
| 756 | }, | ||
| 757 | }; | ||
| 758 | } | ||
| 759 | |||
| 760 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | ||
| 761 | return Instruction{ | ||
| 762 | .branch_exchange = .{ | ||
| 763 | .cond = @enumToInt(cond), | ||
| 764 | .link = link, | ||
| 765 | .rn = rn.id(), | ||
| 766 | }, | ||
| 767 | }; | ||
| 768 | } | ||
| 769 | |||
| 770 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | ||
| 771 | return Instruction{ | ||
| 772 | .supervisor_call = .{ | ||
| 773 | .cond = @enumToInt(cond), | ||
| 774 | .comment = comment, | ||
| 775 | }, | ||
| 776 | }; | ||
| 777 | } | ||
| 778 | |||
| 779 | fn breakpoint(imm: u16) Instruction { | ||
| 780 | return Instruction{ | ||
| 781 | .breakpoint = .{ | ||
| 782 | .imm12 = @truncate(u12, imm >> 4), | ||
| 783 | .imm4 = @truncate(u4, imm), | ||
| 784 | }, | ||
| 785 | }; | ||
| 786 | } | ||
| 787 | |||
| 788 | // Public functions replicating assembler syntax as closely as | ||
| 789 | // possible | ||
| 790 | |||
| 791 | // Data processing | ||
| 792 | |||
| 793 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 794 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); | ||
| 795 | } | ||
| 796 | |||
| 797 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 798 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); | ||
| 799 | } | ||
| 800 | |||
| 801 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 802 | return dataProcessing(cond, .eor, 0, rd, rn, op2); | ||
| 803 | } | ||
| 804 | |||
| 805 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 806 | return dataProcessing(cond, .eor, 1, rd, rn, op2); | ||
| 807 | } | ||
| 808 | |||
| 809 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 810 | return dataProcessing(cond, .sub, 0, rd, rn, op2); | ||
| 811 | } | ||
| 812 | |||
| 813 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 814 | return dataProcessing(cond, .sub, 1, rd, rn, op2); | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 818 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); | ||
| 819 | } | ||
| 820 | |||
| 821 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 822 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 826 | return dataProcessing(cond, .add, 0, rd, rn, op2); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 830 | return dataProcessing(cond, .add, 1, rd, rn, op2); | ||
| 831 | } | ||
| 832 | |||
| 833 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 834 | return dataProcessing(cond, .adc, 0, rd, rn, op2); | ||
| 835 | } | ||
| 836 | |||
| 837 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 838 | return dataProcessing(cond, .adc, 1, rd, rn, op2); | ||
| 839 | } | ||
| 840 | |||
| 841 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 842 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); | ||
| 843 | } | ||
| 844 | |||
| 845 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 846 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); | ||
| 847 | } | ||
| 848 | |||
| 849 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 850 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); | ||
| 851 | } | ||
| 852 | |||
| 853 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 854 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); | ||
| 855 | } | ||
| 856 | |||
| 857 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 858 | return dataProcessing(cond, .tst, 1, .r0, rn, op2); | ||
| 859 | } | ||
| 860 | |||
| 861 | pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 862 | return dataProcessing(cond, .teq, 1, .r0, rn, op2); | ||
| 863 | } | ||
| 864 | |||
| 865 | pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 866 | return dataProcessing(cond, .cmp, 1, .r0, rn, op2); | ||
| 867 | } | ||
| 868 | |||
| 869 | pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { | ||
| 870 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | ||
| 871 | } | ||
| 872 | |||
| 873 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 874 | return dataProcessing(cond, .orr, 0, rd, rn, op2); | ||
| 875 | } | ||
| 876 | |||
| 877 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 878 | return dataProcessing(cond, .orr, 1, rd, rn, op2); | ||
| 879 | } | ||
| 880 | |||
| 881 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 882 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 886 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); | ||
| 887 | } | ||
| 888 | |||
| 889 | pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 890 | return dataProcessing(cond, .bic, 0, rd, rn, op2); | ||
| 891 | } | ||
| 892 | |||
| 893 | pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 894 | return dataProcessing(cond, .bic, 1, rd, rn, op2); | ||
| 895 | } | ||
| 896 | |||
| 897 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 898 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); | ||
| 899 | } | ||
| 900 | |||
| 901 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 902 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | ||
| 903 | } | ||
| 904 | |||
| 905 | // Integer Saturating Arithmetic | ||
| 906 | |||
| 907 | pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 908 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); | ||
| 909 | } | ||
| 910 | |||
| 911 | pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 912 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); | ||
| 913 | } | ||
| 914 | |||
| 915 | pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 916 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); | ||
| 917 | } | ||
| 918 | |||
| 919 | pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | ||
| 920 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); | ||
| 921 | } | ||
| 922 | |||
| 923 | // movw and movt | ||
| 924 | |||
| 925 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { | ||
| 926 | return specialMov(cond, rd, imm, false); | ||
| 927 | } | ||
| 928 | |||
| 929 | pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction { | ||
| 930 | return specialMov(cond, rd, imm, true); | ||
| 931 | } | ||
| 932 | |||
| 933 | // PSR transfer | ||
| 934 | |||
| 935 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | ||
| 936 | return Instruction{ | ||
| 937 | .data_processing = .{ | ||
| 938 | .cond = @enumToInt(cond), | ||
| 939 | .i = 0, | ||
| 940 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, | ||
| 941 | .s = 0, | ||
| 942 | .rn = 0b1111, | ||
| 943 | .rd = rd.id(), | ||
| 944 | .op2 = 0b0000_0000_0000, | ||
| 945 | }, | ||
| 946 | }; | ||
| 947 | } | ||
| 948 | |||
| 949 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { | ||
| 950 | return Instruction{ | ||
| 951 | .data_processing = .{ | ||
| 952 | .cond = @enumToInt(cond), | ||
| 953 | .i = 0, | ||
| 954 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, | ||
| 955 | .s = 0, | ||
| 956 | .rn = 0b1111, | ||
| 957 | .rd = 0b1111, | ||
| 958 | .op2 = op.toU12(), | ||
| 959 | }, | ||
| 960 | }; | ||
| 961 | } | ||
| 962 | |||
| 963 | // Multiply | ||
| 964 | |||
| 965 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | ||
| 966 | return multiply(cond, 0, rd, rn, rm, null); | ||
| 967 | } | ||
| 968 | |||
| 969 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | ||
| 970 | return multiply(cond, 1, rd, rn, rm, null); | ||
| 971 | } | ||
| 972 | |||
| 973 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | ||
| 974 | return multiply(cond, 0, rd, rn, rm, ra); | ||
| 975 | } | ||
| 976 | |||
| 977 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | ||
| 978 | return multiply(cond, 1, rd, rn, rm, ra); | ||
| 979 | } | ||
| 980 | |||
| 981 | // Multiply long | ||
| 982 | |||
| 983 | pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 984 | return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn); | ||
| 985 | } | ||
| 986 | |||
| 987 | pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 988 | return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn); | ||
| 989 | } | ||
| 990 | |||
| 991 | pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 992 | return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn); | ||
| 993 | } | ||
| 994 | |||
| 995 | pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 996 | return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn); | ||
| 997 | } | ||
| 998 | |||
| 999 | pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1000 | return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1004 | return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1008 | return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | ||
| 1012 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | // Single data transfer | ||
| 1016 | |||
| 1017 | pub const OffsetArgs = struct { | ||
| 1018 | pre_index: bool = true, | ||
| 1019 | positive: bool = true, | ||
| 1020 | offset: Offset, | ||
| 1021 | write_back: bool = false, | ||
| 1022 | }; | ||
| 1023 | |||
| 1024 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1025 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1029 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1033 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 1037 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | // Extra load/store | ||
| 1041 | |||
| 1042 | pub const ExtraLoadStoreOffsetArgs = struct { | ||
| 1043 | pre_index: bool = true, | ||
| 1044 | positive: bool = true, | ||
| 1045 | offset: ExtraLoadStoreOffset, | ||
| 1046 | write_back: bool = false, | ||
| 1047 | }; | ||
| 1048 | |||
| 1049 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | ||
| 1050 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | ||
| 1054 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | // Block data transfer | ||
| 1058 | |||
| 1059 | pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1060 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1064 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1068 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1072 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | pub const ldmfa = ldmda; | ||
| 1076 | pub const ldmea = ldmdb; | ||
| 1077 | pub const ldmed = ldmib; | ||
| 1078 | pub const ldmfd = ldmia; | ||
| 1079 | pub const ldm = ldmia; | ||
| 1080 | |||
| 1081 | pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1082 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1086 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1090 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 1094 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | pub const stmed = stmda; | ||
| 1098 | pub const stmfd = stmdb; | ||
| 1099 | pub const stmfa = stmib; | ||
| 1100 | pub const stmea = stmia; | ||
| 1101 | pub const stm = stmia; | ||
| 1102 | |||
| 1103 | // Branch | ||
| 1104 | |||
| 1105 | pub fn b(cond: Condition, offset: i26) Instruction { | ||
| 1106 | return branch(cond, offset, 0); | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | pub fn bl(cond: Condition, offset: i26) Instruction { | ||
| 1110 | return branch(cond, offset, 1); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | // Branch and exchange | ||
| 1114 | |||
| 1115 | pub fn bx(cond: Condition, rn: Register) Instruction { | ||
| 1116 | return branchExchange(cond, rn, 0); | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | pub fn blx(cond: Condition, rn: Register) Instruction { | ||
| 1120 | return branchExchange(cond, rn, 1); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | // Supervisor Call | ||
| 1124 | |||
| 1125 | pub const swi = svc; | ||
| 1126 | |||
| 1127 | pub fn svc(cond: Condition, comment: u24) Instruction { | ||
| 1128 | return supervisorCall(cond, comment); | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | // Breakpoint | ||
| 1132 | |||
| 1133 | pub fn bkpt(imm: u16) Instruction { | ||
| 1134 | return breakpoint(imm); | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | // Aliases | ||
| 1138 | |||
| 1139 | pub fn nop() Instruction { | ||
| 1140 | return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | pub fn pop(cond: Condition, args: anytype) Instruction { | ||
| 1144 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 1145 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | if (args.len < 1) { | ||
| 1149 | @compileError("Expected at least one register"); | ||
| 1150 | } else if (args.len == 1) { | ||
| 1151 | const reg = args[0]; | ||
| 1152 | return ldr(cond, reg, .sp, .{ | ||
| 1153 | .pre_index = false, | ||
| 1154 | .positive = true, | ||
| 1155 | .offset = Offset.imm(4), | ||
| 1156 | .write_back = false, | ||
| 1157 | }); | ||
| 1158 | } else { | ||
| 1159 | var register_list: u16 = 0; | ||
| 1160 | inline for (args) |arg| { | ||
| 1161 | const reg = @as(Register, arg); | ||
| 1162 | register_list |= @as(u16, 1) << reg.id(); | ||
| 1163 | } | ||
| 1164 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 1165 | } | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | pub fn push(cond: Condition, args: anytype) Instruction { | ||
| 1169 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 1170 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | if (args.len < 1) { | ||
| 1174 | @compileError("Expected at least one register"); | ||
| 1175 | } else if (args.len == 1) { | ||
| 1176 | const reg = args[0]; | ||
| 1177 | return str(cond, reg, .sp, .{ | ||
| 1178 | .pre_index = true, | ||
| 1179 | .positive = false, | ||
| 1180 | .offset = Offset.imm(4), | ||
| 1181 | .write_back = true, | ||
| 1182 | }); | ||
| 1183 | } else { | ||
| 1184 | var register_list: u16 = 0; | ||
| 1185 | inline for (args) |arg| { | ||
| 1186 | const reg = @as(Register, arg); | ||
| 1187 | register_list |= @as(u16, 1) << reg.id(); | ||
| 1188 | } | ||
| 1189 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 1190 | } | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | pub const ShiftAmount = union(enum) { | ||
| 1194 | immediate: u5, | ||
| 1195 | register: Register, | ||
| 1196 | |||
| 1197 | pub fn imm(immediate: u5) ShiftAmount { | ||
| 1198 | return .{ | ||
| 1199 | .immediate = immediate, | ||
| 1200 | }; | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | pub fn reg(register: Register) ShiftAmount { | ||
| 1204 | return .{ | ||
| 1205 | .register = register, | ||
| 1206 | }; | ||
| 1207 | } | ||
| 1208 | }; | ||
| 1209 | |||
| 1210 | pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1211 | return switch (shift) { | ||
| 1212 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | ||
| 1213 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | ||
| 1214 | }; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1218 | return switch (shift) { | ||
| 1219 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | ||
| 1220 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | ||
| 1221 | }; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1225 | return switch (shift) { | ||
| 1226 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | ||
| 1227 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | ||
| 1228 | }; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1232 | return switch (shift) { | ||
| 1233 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | ||
| 1234 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | ||
| 1235 | }; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1239 | return switch (shift) { | ||
| 1240 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | ||
| 1241 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | ||
| 1242 | }; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1246 | return switch (shift) { | ||
| 1247 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | ||
| 1248 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | ||
| 1249 | }; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1253 | return switch (shift) { | ||
| 1254 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | ||
| 1255 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | ||
| 1256 | }; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | ||
| 1260 | return switch (shift) { | ||
| 1261 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | ||
| 1262 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | ||
| 1263 | }; | ||
| 1264 | } | ||
| 1265 | }; | ||
| 1266 | |||
| 1267 | test "serialize instructions" { | ||
| 1268 | const Testcase = struct { | ||
| 1269 | inst: Instruction, | ||
| 1270 | expected: u32, | ||
| 1271 | }; | ||
| 1272 | |||
| 1273 | const testcases = [_]Testcase{ | ||
| 1274 | .{ // add r0, r0, r0 | ||
| 1275 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | ||
| 1276 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | ||
| 1277 | }, | ||
| 1278 | .{ // mov r4, r2 | ||
| 1279 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | ||
| 1280 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | ||
| 1281 | }, | ||
| 1282 | .{ // mov r0, #42 | ||
| 1283 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), | ||
| 1284 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | ||
| 1285 | }, | ||
| 1286 | .{ // mrs r5, cpsr | ||
| 1287 | .inst = Instruction.mrs(.al, .r5, .cpsr), | ||
| 1288 | .expected = 0b1110_00010_0_001111_0101_000000000000, | ||
| 1289 | }, | ||
| 1290 | .{ // mul r0, r1, r2 | ||
| 1291 | .inst = Instruction.mul(.al, .r0, .r1, .r2), | ||
| 1292 | .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001, | ||
| 1293 | }, | ||
| 1294 | .{ // umlal r0, r1, r5, r6 | ||
| 1295 | .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6), | ||
| 1296 | .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101, | ||
| 1297 | }, | ||
| 1298 | .{ // ldr r0, [r2, #42] | ||
| 1299 | .inst = Instruction.ldr(.al, .r0, .r2, .{ | ||
| 1300 | .offset = Instruction.Offset.imm(42), | ||
| 1301 | }), | ||
| 1302 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | ||
| 1303 | }, | ||
| 1304 | .{ // str r0, [r3] | ||
| 1305 | .inst = Instruction.str(.al, .r0, .r3, .{ | ||
| 1306 | .offset = Instruction.Offset.none, | ||
| 1307 | }), | ||
| 1308 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | ||
| 1309 | }, | ||
| 1310 | .{ // strh r1, [r5] | ||
| 1311 | .inst = Instruction.strh(.al, .r1, .r5, .{ | ||
| 1312 | .offset = Instruction.ExtraLoadStoreOffset.none, | ||
| 1313 | }), | ||
| 1314 | .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000, | ||
| 1315 | }, | ||
| 1316 | .{ // b #12 | ||
| 1317 | .inst = Instruction.b(.al, 12), | ||
| 1318 | .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011, | ||
| 1319 | }, | ||
| 1320 | .{ // bl #-4 | ||
| 1321 | .inst = Instruction.bl(.al, -4), | ||
| 1322 | .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111, | ||
| 1323 | }, | ||
| 1324 | .{ // bx lr | ||
| 1325 | .inst = Instruction.bx(.al, .lr), | ||
| 1326 | .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, | ||
| 1327 | }, | ||
| 1328 | .{ // svc #0 | ||
| 1329 | .inst = Instruction.svc(.al, 0), | ||
| 1330 | .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, | ||
| 1331 | }, | ||
| 1332 | .{ // bkpt #42 | ||
| 1333 | .inst = Instruction.bkpt(42), | ||
| 1334 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | ||
| 1335 | }, | ||
| 1336 | .{ // stmdb r9, {r0} | ||
| 1337 | .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), | ||
| 1338 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, | ||
| 1339 | }, | ||
| 1340 | .{ // ldmea r4!, {r2, r5} | ||
| 1341 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | ||
| 1342 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | ||
| 1343 | }, | ||
| 1344 | .{ // qadd r0, r7, r8 | ||
| 1345 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), | ||
| 1346 | .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, | ||
| 1347 | }, | ||
| 1348 | }; | ||
| 1349 | |||
| 1350 | for (testcases) |case| { | ||
| 1351 | const actual = case.inst.toU32(); | ||
| 1352 | try testing.expectEqual(case.expected, actual); | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | test "aliases" { | ||
| 1357 | const Testcase = struct { | ||
| 1358 | expected: Instruction, | ||
| 1359 | actual: Instruction, | ||
| 1360 | }; | ||
| 1361 | |||
| 1362 | const testcases = [_]Testcase{ | ||
| 1363 | .{ // pop { r6 } | ||
| 1364 | .actual = Instruction.pop(.al, .{.r6}), | ||
| 1365 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | ||
| 1366 | .pre_index = false, | ||
| 1367 | .positive = true, | ||
| 1368 | .offset = Instruction.Offset.imm(4), | ||
| 1369 | .write_back = false, | ||
| 1370 | }), | ||
| 1371 | }, | ||
| 1372 | .{ // pop { r1, r5 } | ||
| 1373 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), | ||
| 1374 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), | ||
| 1375 | }, | ||
| 1376 | .{ // push { r3 } | ||
| 1377 | .actual = Instruction.push(.al, .{.r3}), | ||
| 1378 | .expected = Instruction.str(.al, .r3, .sp, .{ | ||
| 1379 | .pre_index = true, | ||
| 1380 | .positive = false, | ||
| 1381 | .offset = Instruction.Offset.imm(4), | ||
| 1382 | .write_back = true, | ||
| 1383 | }), | ||
| 1384 | }, | ||
| 1385 | .{ // push { r0, r2 } | ||
| 1386 | .actual = Instruction.push(.al, .{ .r0, .r2 }), | ||
| 1387 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), | ||
| 1388 | }, | ||
| 1389 | .{ // lsl r4, r5, #5 | ||
| 1390 | .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), | ||
| 1391 | .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( | ||
| 1392 | .r5, | ||
| 1393 | Instruction.Operand.Shift.imm(5, .logical_left), | ||
| 1394 | )), | ||
| 1395 | }, | ||
| 1396 | .{ // asrs r1, r1, r3 | ||
| 1397 | .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), | ||
| 1398 | .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( | ||
| 1399 | .r1, | ||
| 1400 | Instruction.Operand.Shift.reg(.r3, .arithmetic_right), | ||
| 1401 | )), | ||
| 1402 | }, | ||
| 1403 | }; | ||
| 1404 | |||
| 1405 | for (testcases) |case| { | ||
| 1406 | try testing.expectEqual(case.expected.toU32(), case.actual.toU32()); | ||
| 1407 | } | ||
| 1408 | } | ||
src/codegen/riscv64.zig deleted-470| ... | @@ -1,470 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | // TODO: this is only tagged to facilitate the monstrosity. | ||
| 7 | // Once packed structs work make it packed. | ||
| 8 | pub const Instruction = union(enum) { | ||
| 9 | R: packed struct { | ||
| 10 | opcode: u7, | ||
| 11 | rd: u5, | ||
| 12 | funct3: u3, | ||
| 13 | rs1: u5, | ||
| 14 | rs2: u5, | ||
| 15 | funct7: u7, | ||
| 16 | }, | ||
| 17 | I: packed struct { | ||
| 18 | opcode: u7, | ||
| 19 | rd: u5, | ||
| 20 | funct3: u3, | ||
| 21 | rs1: u5, | ||
| 22 | imm0_11: u12, | ||
| 23 | }, | ||
| 24 | S: packed struct { | ||
| 25 | opcode: u7, | ||
| 26 | imm0_4: u5, | ||
| 27 | funct3: u3, | ||
| 28 | rs1: u5, | ||
| 29 | rs2: u5, | ||
| 30 | imm5_11: u7, | ||
| 31 | }, | ||
| 32 | B: packed struct { | ||
| 33 | opcode: u7, | ||
| 34 | imm11: u1, | ||
| 35 | imm1_4: u4, | ||
| 36 | funct3: u3, | ||
| 37 | rs1: u5, | ||
| 38 | rs2: u5, | ||
| 39 | imm5_10: u6, | ||
| 40 | imm12: u1, | ||
| 41 | }, | ||
| 42 | U: packed struct { | ||
| 43 | opcode: u7, | ||
| 44 | rd: u5, | ||
| 45 | imm12_31: u20, | ||
| 46 | }, | ||
| 47 | J: packed struct { | ||
| 48 | opcode: u7, | ||
| 49 | rd: u5, | ||
| 50 | imm12_19: u8, | ||
| 51 | imm11: u1, | ||
| 52 | imm1_10: u10, | ||
| 53 | imm20: u1, | ||
| 54 | }, | ||
| 55 | |||
| 56 | // TODO: once packed structs work we can remove this monstrosity. | ||
| 57 | pub fn toU32(self: Instruction) u32 { | ||
| 58 | return switch (self) { | ||
| 59 | .R => |v| @bitCast(u32, v), | ||
| 60 | .I => |v| @bitCast(u32, v), | ||
| 61 | .S => |v| @bitCast(u32, v), | ||
| 62 | .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31), | ||
| 63 | .U => |v| @bitCast(u32, v), | ||
| 64 | .J => |v| @bitCast(u32, v), | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | |||
| 68 | fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction { | ||
| 69 | return Instruction{ | ||
| 70 | .R = .{ | ||
| 71 | .opcode = op, | ||
| 72 | .funct3 = fn3, | ||
| 73 | .funct7 = fn7, | ||
| 74 | .rd = @enumToInt(rd), | ||
| 75 | .rs1 = @enumToInt(r1), | ||
| 76 | .rs2 = @enumToInt(r2), | ||
| 77 | }, | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | // RISC-V is all signed all the time -- convert immediates to unsigned for processing | ||
| 82 | fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction { | ||
| 83 | const umm = @bitCast(u12, imm); | ||
| 84 | |||
| 85 | return Instruction{ | ||
| 86 | .I = .{ | ||
| 87 | .opcode = op, | ||
| 88 | .funct3 = fn3, | ||
| 89 | .rd = @enumToInt(rd), | ||
| 90 | .rs1 = @enumToInt(r1), | ||
| 91 | .imm0_11 = umm, | ||
| 92 | }, | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | |||
| 96 | fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction { | ||
| 97 | const umm = @bitCast(u12, imm); | ||
| 98 | |||
| 99 | return Instruction{ | ||
| 100 | .S = .{ | ||
| 101 | .opcode = op, | ||
| 102 | .funct3 = fn3, | ||
| 103 | .rs1 = @enumToInt(r1), | ||
| 104 | .rs2 = @enumToInt(r2), | ||
| 105 | .imm0_4 = @truncate(u5, umm), | ||
| 106 | .imm5_11 = @truncate(u7, umm >> 5), | ||
| 107 | }, | ||
| 108 | }; | ||
| 109 | } | ||
| 110 | |||
| 111 | // Use significance value rather than bit value, same for J-type | ||
| 112 | // -- less burden on callsite, bonus semantic checking | ||
| 113 | fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { | ||
| 114 | const umm = @bitCast(u13, imm); | ||
| 115 | assert(umm % 2 == 0); // misaligned branch target | ||
| 116 | |||
| 117 | return Instruction{ | ||
| 118 | .B = .{ | ||
| 119 | .opcode = op, | ||
| 120 | .funct3 = fn3, | ||
| 121 | .rs1 = @enumToInt(r1), | ||
| 122 | .rs2 = @enumToInt(r2), | ||
| 123 | .imm1_4 = @truncate(u4, umm >> 1), | ||
| 124 | .imm5_10 = @truncate(u6, umm >> 5), | ||
| 125 | .imm11 = @truncate(u1, umm >> 11), | ||
| 126 | .imm12 = @truncate(u1, umm >> 12), | ||
| 127 | }, | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | // We have to extract the 20 bits anyway -- let's not make it more painful | ||
| 132 | fn uType(op: u7, rd: Register, imm: i20) Instruction { | ||
| 133 | const umm = @bitCast(u20, imm); | ||
| 134 | |||
| 135 | return Instruction{ | ||
| 136 | .U = .{ | ||
| 137 | .opcode = op, | ||
| 138 | .rd = @enumToInt(rd), | ||
| 139 | .imm12_31 = umm, | ||
| 140 | }, | ||
| 141 | }; | ||
| 142 | } | ||
| 143 | |||
| 144 | fn jType(op: u7, rd: Register, imm: i21) Instruction { | ||
| 145 | const umm = @bitCast(u21, imm); | ||
| 146 | assert(umm % 2 == 0); // misaligned jump target | ||
| 147 | |||
| 148 | return Instruction{ | ||
| 149 | .J = .{ | ||
| 150 | .opcode = op, | ||
| 151 | .rd = @enumToInt(rd), | ||
| 152 | .imm1_10 = @truncate(u10, umm >> 1), | ||
| 153 | .imm11 = @truncate(u1, umm >> 11), | ||
| 154 | .imm12_19 = @truncate(u8, umm >> 12), | ||
| 155 | .imm20 = @truncate(u1, umm >> 20), | ||
| 156 | }, | ||
| 157 | }; | ||
| 158 | } | ||
| 159 | |||
| 160 | // The meat and potatoes. Arguments are in the order in which they would appear in assembly code. | ||
| 161 | |||
| 162 | // Arithmetic/Logical, Register-Register | ||
| 163 | |||
| 164 | pub fn add(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 165 | return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2); | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn sub(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 169 | return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 173 | return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2); | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 177 | return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2); | ||
| 178 | } | ||
| 179 | |||
| 180 | pub fn xor(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 181 | return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn sll(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 185 | return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2); | ||
| 186 | } | ||
| 187 | |||
| 188 | pub fn srl(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 189 | return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2); | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn sra(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 193 | return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2); | ||
| 194 | } | ||
| 195 | |||
| 196 | pub fn slt(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 197 | return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2); | ||
| 198 | } | ||
| 199 | |||
| 200 | pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 201 | return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2); | ||
| 202 | } | ||
| 203 | |||
| 204 | // Arithmetic/Logical, Register-Register (32-bit) | ||
| 205 | |||
| 206 | pub fn addw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 207 | return rType(0b0111011, 0b000, rd, r1, r2); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn subw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 211 | return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2); | ||
| 212 | } | ||
| 213 | |||
| 214 | pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 215 | return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2); | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 219 | return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2); | ||
| 220 | } | ||
| 221 | |||
| 222 | pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction { | ||
| 223 | return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2); | ||
| 224 | } | ||
| 225 | |||
| 226 | // Arithmetic/Logical, Register-Immediate | ||
| 227 | |||
| 228 | pub fn addi(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 229 | return iType(0b0010011, 0b000, rd, r1, imm); | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn andi(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 233 | return iType(0b0010011, 0b111, rd, r1, imm); | ||
| 234 | } | ||
| 235 | |||
| 236 | pub fn ori(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 237 | return iType(0b0010011, 0b110, rd, r1, imm); | ||
| 238 | } | ||
| 239 | |||
| 240 | pub fn xori(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 241 | return iType(0b0010011, 0b100, rd, r1, imm); | ||
| 242 | } | ||
| 243 | |||
| 244 | pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 245 | return iType(0b0010011, 0b001, rd, r1, shamt); | ||
| 246 | } | ||
| 247 | |||
| 248 | pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 249 | return iType(0b0010011, 0b101, rd, r1, shamt); | ||
| 250 | } | ||
| 251 | |||
| 252 | pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction { | ||
| 253 | return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt); | ||
| 254 | } | ||
| 255 | |||
| 256 | pub fn slti(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 257 | return iType(0b0010011, 0b010, rd, r1, imm); | ||
| 258 | } | ||
| 259 | |||
| 260 | pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction { | ||
| 261 | return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm)); | ||
| 262 | } | ||
| 263 | |||
| 264 | // Arithmetic/Logical, Register-Immediate (32-bit) | ||
| 265 | |||
| 266 | pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction { | ||
| 267 | return iType(0b0011011, 0b000, rd, r1, imm); | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 271 | return iType(0b0011011, 0b001, rd, r1, shamt); | ||
| 272 | } | ||
| 273 | |||
| 274 | pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 275 | return iType(0b0011011, 0b101, rd, r1, shamt); | ||
| 276 | } | ||
| 277 | |||
| 278 | pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction { | ||
| 279 | return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt); | ||
| 280 | } | ||
| 281 | |||
| 282 | // Upper Immediate | ||
| 283 | |||
| 284 | pub fn lui(rd: Register, imm: i20) Instruction { | ||
| 285 | return uType(0b0110111, rd, imm); | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn auipc(rd: Register, imm: i20) Instruction { | ||
| 289 | return uType(0b0010111, rd, imm); | ||
| 290 | } | ||
| 291 | |||
| 292 | // Load | ||
| 293 | |||
| 294 | pub fn ld(rd: Register, offset: i12, base: Register) Instruction { | ||
| 295 | return iType(0b0000011, 0b011, rd, base, offset); | ||
| 296 | } | ||
| 297 | |||
| 298 | pub fn lw(rd: Register, offset: i12, base: Register) Instruction { | ||
| 299 | return iType(0b0000011, 0b010, rd, base, offset); | ||
| 300 | } | ||
| 301 | |||
| 302 | pub fn lwu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 303 | return iType(0b0000011, 0b110, rd, base, offset); | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn lh(rd: Register, offset: i12, base: Register) Instruction { | ||
| 307 | return iType(0b0000011, 0b001, rd, base, offset); | ||
| 308 | } | ||
| 309 | |||
| 310 | pub fn lhu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 311 | return iType(0b0000011, 0b101, rd, base, offset); | ||
| 312 | } | ||
| 313 | |||
| 314 | pub fn lb(rd: Register, offset: i12, base: Register) Instruction { | ||
| 315 | return iType(0b0000011, 0b000, rd, base, offset); | ||
| 316 | } | ||
| 317 | |||
| 318 | pub fn lbu(rd: Register, offset: i12, base: Register) Instruction { | ||
| 319 | return iType(0b0000011, 0b100, rd, base, offset); | ||
| 320 | } | ||
| 321 | |||
| 322 | // Store | ||
| 323 | |||
| 324 | pub fn sd(rs: Register, offset: i12, base: Register) Instruction { | ||
| 325 | return sType(0b0100011, 0b011, base, rs, offset); | ||
| 326 | } | ||
| 327 | |||
| 328 | pub fn sw(rs: Register, offset: i12, base: Register) Instruction { | ||
| 329 | return sType(0b0100011, 0b010, base, rs, offset); | ||
| 330 | } | ||
| 331 | |||
| 332 | pub fn sh(rs: Register, offset: i12, base: Register) Instruction { | ||
| 333 | return sType(0b0100011, 0b001, base, rs, offset); | ||
| 334 | } | ||
| 335 | |||
| 336 | pub fn sb(rs: Register, offset: i12, base: Register) Instruction { | ||
| 337 | return sType(0b0100011, 0b000, base, rs, offset); | ||
| 338 | } | ||
| 339 | |||
| 340 | // Fence | ||
| 341 | // TODO: implement fence | ||
| 342 | |||
| 343 | // Branch | ||
| 344 | |||
| 345 | pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 346 | return bType(0b1100011, 0b000, r1, r2, offset); | ||
| 347 | } | ||
| 348 | |||
| 349 | pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 350 | return bType(0b1100011, 0b001, r1, r2, offset); | ||
| 351 | } | ||
| 352 | |||
| 353 | pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 354 | return bType(0b1100011, 0b100, r1, r2, offset); | ||
| 355 | } | ||
| 356 | |||
| 357 | pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 358 | return bType(0b1100011, 0b101, r1, r2, offset); | ||
| 359 | } | ||
| 360 | |||
| 361 | pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 362 | return bType(0b1100011, 0b110, r1, r2, offset); | ||
| 363 | } | ||
| 364 | |||
| 365 | pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { | ||
| 366 | return bType(0b1100011, 0b111, r1, r2, offset); | ||
| 367 | } | ||
| 368 | |||
| 369 | // Jump | ||
| 370 | |||
| 371 | pub fn jal(link: Register, offset: i21) Instruction { | ||
| 372 | return jType(0b1101111, link, offset); | ||
| 373 | } | ||
| 374 | |||
| 375 | pub fn jalr(link: Register, offset: i12, base: Register) Instruction { | ||
| 376 | return iType(0b1100111, 0b000, link, base, offset); | ||
| 377 | } | ||
| 378 | |||
| 379 | // System | ||
| 380 | |||
| 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); | ||
| 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); | ||
| 383 | }; | ||
| 384 | |||
| 385 | // zig fmt: off | ||
| 386 | pub const RawRegister = enum(u5) { | ||
| 387 | x0, x1, x2, x3, x4, x5, x6, x7, | ||
| 388 | x8, x9, x10, x11, x12, x13, x14, x15, | ||
| 389 | x16, x17, x18, x19, x20, x21, x22, x23, | ||
| 390 | x24, x25, x26, x27, x28, x29, x30, x31, | ||
| 391 | |||
| 392 | pub fn dwarfLocOp(reg: RawRegister) u8 { | ||
| 393 | return @enumToInt(reg) + DW.OP.reg0; | ||
| 394 | } | ||
| 395 | }; | ||
| 396 | |||
| 397 | pub const Register = enum(u5) { | ||
| 398 | // 64 bit registers | ||
| 399 | zero, // zero | ||
| 400 | ra, // return address. caller saved | ||
| 401 | sp, // stack pointer. callee saved. | ||
| 402 | gp, // global pointer | ||
| 403 | tp, // thread pointer | ||
| 404 | t0, t1, t2, // temporaries. caller saved. | ||
| 405 | s0, // s0/fp, callee saved. | ||
| 406 | s1, // callee saved. | ||
| 407 | a0, a1, // fn args/return values. caller saved. | ||
| 408 | a2, a3, a4, a5, a6, a7, // fn args. caller saved. | ||
| 409 | s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. | ||
| 410 | t3, t4, t5, t6, // caller saved | ||
| 411 | |||
| 412 | pub fn parseRegName(name: []const u8) ?Register { | ||
| 413 | if(std.meta.stringToEnum(Register, name)) |reg| return reg; | ||
| 414 | if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); | ||
| 415 | return null; | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Returns the index into `callee_preserved_regs`. | ||
| 419 | pub fn allocIndex(self: Register) ?u4 { | ||
| 420 | inline for(callee_preserved_regs) |cpreg, i| { | ||
| 421 | if(self == cpreg) return i; | ||
| 422 | } | ||
| 423 | return null; | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn dwarfLocOp(reg: Register) u8 { | ||
| 427 | return @as(u8, @enumToInt(reg)) + DW.OP.reg0; | ||
| 428 | } | ||
| 429 | }; | ||
| 430 | |||
| 431 | // zig fmt: on | ||
| 432 | |||
| 433 | pub const callee_preserved_regs = [_]Register{ | ||
| 434 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | ||
| 435 | }; | ||
| 436 | |||
| 437 | test "serialize instructions" { | ||
| 438 | const Testcase = struct { | ||
| 439 | inst: Instruction, | ||
| 440 | expected: u32, | ||
| 441 | }; | ||
| 442 | |||
| 443 | const testcases = [_]Testcase{ | ||
| 444 | .{ // add t6, zero, zero | ||
| 445 | .inst = Instruction.add(.t6, .zero, .zero), | ||
| 446 | .expected = 0b0000000_00000_00000_000_11111_0110011, | ||
| 447 | }, | ||
| 448 | .{ // sd s0, 0x7f(s0) | ||
| 449 | .inst = Instruction.sd(.s0, 0x7f, .s0), | ||
| 450 | .expected = 0b0000011_01000_01000_011_11111_0100011, | ||
| 451 | }, | ||
| 452 | .{ // bne s0, s1, 0x42 | ||
| 453 | .inst = Instruction.bne(.s0, .s1, 0x42), | ||
| 454 | .expected = 0b0_000010_01001_01000_001_0001_0_1100011, | ||
| 455 | }, | ||
| 456 | .{ // j 0x1a | ||
| 457 | .inst = Instruction.jal(.zero, 0x1a), | ||
| 458 | .expected = 0b0_0000001101_0_00000000_00000_1101111, | ||
| 459 | }, | ||
| 460 | .{ // ebreak | ||
| 461 | .inst = Instruction.ebreak, | ||
| 462 | .expected = 0b000000000001_00000_000_00000_1110011, | ||
| 463 | }, | ||
| 464 | }; | ||
| 465 | |||
| 466 | for (testcases) |case| { | ||
| 467 | const actual = case.inst.toU32(); | ||
| 468 | try testing.expectEqual(case.expected, actual); | ||
| 469 | } | ||
| 470 | } | ||
src/codegen/x86.zig deleted-123| ... | @@ -1,123 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | |||
| 4 | // zig fmt: off | ||
| 5 | pub const Register = enum(u8) { | ||
| 6 | // 0 through 7, 32-bit registers. id is int value | ||
| 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | ||
| 8 | |||
| 9 | // 8-15, 16-bit registers. id is int value - 8. | ||
| 10 | ax, cx, dx, bx, sp, bp, si, di, | ||
| 11 | |||
| 12 | // 16-23, 8-bit registers. id is int value - 16. | ||
| 13 | al, cl, dl, bl, ah, ch, dh, bh, | ||
| 14 | |||
| 15 | /// Returns the bit-width of the register. | ||
| 16 | pub fn size(self: @This()) u7 { | ||
| 17 | return switch (@enumToInt(self)) { | ||
| 18 | 0...7 => 32, | ||
| 19 | 8...15 => 16, | ||
| 20 | 16...23 => 8, | ||
| 21 | else => unreachable, | ||
| 22 | }; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Returns the register's id. This is used in practically every opcode the | ||
| 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move | ||
| 27 | /// instruction, and is used in the R/M byte. | ||
| 28 | pub fn id(self: @This()) u3 { | ||
| 29 | return @truncate(u3, @enumToInt(self)); | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Returns the index into `callee_preserved_regs`. | ||
| 33 | pub fn allocIndex(self: Register) ?u4 { | ||
| 34 | return switch (self) { | ||
| 35 | .eax, .ax, .al => 0, | ||
| 36 | .ecx, .cx, .cl => 1, | ||
| 37 | .edx, .dx, .dl => 2, | ||
| 38 | .esi, .si => 3, | ||
| 39 | .edi, .di => 4, | ||
| 40 | else => null, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Convert from any register to its 32 bit alias. | ||
| 45 | pub fn to32(self: Register) Register { | ||
| 46 | return @intToEnum(Register, @as(u8, self.id())); | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Convert from any register to its 16 bit alias. | ||
| 50 | pub fn to16(self: Register) Register { | ||
| 51 | return @intToEnum(Register, @as(u8, self.id()) + 8); | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Convert from any register to its 8 bit alias. | ||
| 55 | pub fn to8(self: Register) Register { | ||
| 56 | return @intToEnum(Register, @as(u8, self.id()) + 16); | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | pub fn dwarfLocOp(reg: Register) u8 { | ||
| 61 | return switch (reg.to32()) { | ||
| 62 | .eax => DW.OP.reg0, | ||
| 63 | .ecx => DW.OP.reg1, | ||
| 64 | .edx => DW.OP.reg2, | ||
| 65 | .ebx => DW.OP.reg3, | ||
| 66 | .esp => DW.OP.reg4, | ||
| 67 | .ebp => DW.OP.reg5, | ||
| 68 | .esi => DW.OP.reg6, | ||
| 69 | .edi => DW.OP.reg7, | ||
| 70 | else => unreachable, | ||
| 71 | }; | ||
| 72 | } | ||
| 73 | }; | ||
| 74 | |||
| 75 | // zig fmt: on | ||
| 76 | |||
| 77 | pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; | ||
| 78 | |||
| 79 | // TODO add these to Register enum and corresponding dwarfLocOp | ||
| 80 | // // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. | ||
| 81 | // RA = (8, "RA"), | ||
| 82 | // | ||
| 83 | // ST0 = (11, "st0"), | ||
| 84 | // ST1 = (12, "st1"), | ||
| 85 | // ST2 = (13, "st2"), | ||
| 86 | // ST3 = (14, "st3"), | ||
| 87 | // ST4 = (15, "st4"), | ||
| 88 | // ST5 = (16, "st5"), | ||
| 89 | // ST6 = (17, "st6"), | ||
| 90 | // ST7 = (18, "st7"), | ||
| 91 | // | ||
| 92 | // XMM0 = (21, "xmm0"), | ||
| 93 | // XMM1 = (22, "xmm1"), | ||
| 94 | // XMM2 = (23, "xmm2"), | ||
| 95 | // XMM3 = (24, "xmm3"), | ||
| 96 | // XMM4 = (25, "xmm4"), | ||
| 97 | // XMM5 = (26, "xmm5"), | ||
| 98 | // XMM6 = (27, "xmm6"), | ||
| 99 | // XMM7 = (28, "xmm7"), | ||
| 100 | // | ||
| 101 | // MM0 = (29, "mm0"), | ||
| 102 | // MM1 = (30, "mm1"), | ||
| 103 | // MM2 = (31, "mm2"), | ||
| 104 | // MM3 = (32, "mm3"), | ||
| 105 | // MM4 = (33, "mm4"), | ||
| 106 | // MM5 = (34, "mm5"), | ||
| 107 | // MM6 = (35, "mm6"), | ||
| 108 | // MM7 = (36, "mm7"), | ||
| 109 | // | ||
| 110 | // MXCSR = (39, "mxcsr"), | ||
| 111 | // | ||
| 112 | // ES = (40, "es"), | ||
| 113 | // CS = (41, "cs"), | ||
| 114 | // SS = (42, "ss"), | ||
| 115 | // DS = (43, "ds"), | ||
| 116 | // FS = (44, "fs"), | ||
| 117 | // GS = (45, "gs"), | ||
| 118 | // | ||
| 119 | // TR = (48, "tr"), | ||
| 120 | // LDTR = (49, "ldtr"), | ||
| 121 | // | ||
| 122 | // FS_BASE = (93, "fs.base"), | ||
| 123 | // GS_BASE = (94, "gs.base"), | ||
src/codegen/x86_64.zig deleted-716| ... | @@ -1,716 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const mem = std.mem; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const ArrayList = std.ArrayList; | ||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | const DW = std.dwarf; | ||
| 8 | |||
| 9 | // zig fmt: off | ||
| 10 | |||
| 11 | /// Definitions of all of the x64 registers. The order is semantically meaningful. | ||
| 12 | /// The registers are defined such that IDs go in descending order of 64-bit, | ||
| 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen | ||
| 14 | /// registers. This results in some useful properties: | ||
| 15 | /// | ||
| 16 | /// Any 64-bit register can be turned into its 32-bit form by adding 16, and | ||
| 17 | /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it | ||
| 18 | /// works for all except for sp, bp, si, and di, which do *not* have an 8-bit | ||
| 19 | /// form. | ||
| 20 | /// | ||
| 21 | /// If (register & 8) is set, the register is extended. | ||
| 22 | /// | ||
| 23 | /// The ID can be easily determined by figuring out what range the register is | ||
| 24 | /// in, and then subtracting the base. | ||
| 25 | pub const Register = enum(u8) { | ||
| 26 | // 0 through 15, 64-bit registers. 8-15 are extended. | ||
| 27 | // id is just the int value. | ||
| 28 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, | ||
| 29 | r8, r9, r10, r11, r12, r13, r14, r15, | ||
| 30 | |||
| 31 | // 16 through 31, 32-bit registers. 24-31 are extended. | ||
| 32 | // id is int value - 16. | ||
| 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | ||
| 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | ||
| 35 | |||
| 36 | // 32-47, 16-bit registers. 40-47 are extended. | ||
| 37 | // id is int value - 32. | ||
| 38 | ax, cx, dx, bx, sp, bp, si, di, | ||
| 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | ||
| 40 | |||
| 41 | // 48-63, 8-bit registers. 56-63 are extended. | ||
| 42 | // id is int value - 48. | ||
| 43 | al, cl, dl, bl, ah, ch, dh, bh, | ||
| 44 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, | ||
| 45 | |||
| 46 | /// Returns the bit-width of the register. | ||
| 47 | pub fn size(self: Register) u7 { | ||
| 48 | return switch (@enumToInt(self)) { | ||
| 49 | 0...15 => 64, | ||
| 50 | 16...31 => 32, | ||
| 51 | 32...47 => 16, | ||
| 52 | 48...64 => 8, | ||
| 53 | else => unreachable, | ||
| 54 | }; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Returns whether the register is *extended*. Extended registers are the | ||
| 58 | /// new registers added with amd64, r8 through r15. This also includes any | ||
| 59 | /// other variant of access to those registers, such as r8b, r15d, and so | ||
| 60 | /// on. This is needed because access to these registers requires special | ||
| 61 | /// handling via the REX prefix, via the B or R bits, depending on context. | ||
| 62 | pub fn isExtended(self: Register) bool { | ||
| 63 | return @enumToInt(self) & 0x08 != 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | /// This returns the 4-bit register ID, which is used in practically every | ||
| 67 | /// opcode. Note that bit 3 (the highest bit) is *never* used directly in | ||
| 68 | /// an instruction (@see isExtended), and requires special handling. The | ||
| 69 | /// lower three bits are often embedded directly in instructions (such as | ||
| 70 | /// the B8 variant of moves), or used in R/M bytes. | ||
| 71 | pub fn id(self: Register) u4 { | ||
| 72 | return @truncate(u4, @enumToInt(self)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Like id, but only returns the lower 3 bits. | ||
| 76 | pub fn low_id(self: Register) u3 { | ||
| 77 | return @truncate(u3, @enumToInt(self)); | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Returns the index into `callee_preserved_regs`. | ||
| 81 | pub fn allocIndex(self: Register) ?u4 { | ||
| 82 | return switch (self) { | ||
| 83 | .rax, .eax, .ax, .al => 0, | ||
| 84 | .rcx, .ecx, .cx, .cl => 1, | ||
| 85 | .rdx, .edx, .dx, .dl => 2, | ||
| 86 | .rsi, .esi, .si => 3, | ||
| 87 | .rdi, .edi, .di => 4, | ||
| 88 | .r8, .r8d, .r8w, .r8b => 5, | ||
| 89 | .r9, .r9d, .r9w, .r9b => 6, | ||
| 90 | .r10, .r10d, .r10w, .r10b => 7, | ||
| 91 | .r11, .r11d, .r11w, .r11b => 8, | ||
| 92 | else => null, | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Convert from any register to its 64 bit alias. | ||
| 97 | pub fn to64(self: Register) Register { | ||
| 98 | return @intToEnum(Register, self.id()); | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Convert from any register to its 32 bit alias. | ||
| 102 | pub fn to32(self: Register) Register { | ||
| 103 | return @intToEnum(Register, @as(u8, self.id()) + 16); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Convert from any register to its 16 bit alias. | ||
| 107 | pub fn to16(self: Register) Register { | ||
| 108 | return @intToEnum(Register, @as(u8, self.id()) + 32); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Convert from any register to its 8 bit alias. | ||
| 112 | pub fn to8(self: Register) Register { | ||
| 113 | return @intToEnum(Register, @as(u8, self.id()) + 48); | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn dwarfLocOp(self: Register) u8 { | ||
| 117 | return switch (self.to64()) { | ||
| 118 | .rax => DW.OP.reg0, | ||
| 119 | .rdx => DW.OP.reg1, | ||
| 120 | .rcx => DW.OP.reg2, | ||
| 121 | .rbx => DW.OP.reg3, | ||
| 122 | .rsi => DW.OP.reg4, | ||
| 123 | .rdi => DW.OP.reg5, | ||
| 124 | .rbp => DW.OP.reg6, | ||
| 125 | .rsp => DW.OP.reg7, | ||
| 126 | |||
| 127 | .r8 => DW.OP.reg8, | ||
| 128 | .r9 => DW.OP.reg9, | ||
| 129 | .r10 => DW.OP.reg10, | ||
| 130 | .r11 => DW.OP.reg11, | ||
| 131 | .r12 => DW.OP.reg12, | ||
| 132 | .r13 => DW.OP.reg13, | ||
| 133 | .r14 => DW.OP.reg14, | ||
| 134 | .r15 => DW.OP.reg15, | ||
| 135 | |||
| 136 | else => unreachable, | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | }; | ||
| 140 | |||
| 141 | // zig fmt: on | ||
| 142 | |||
| 143 | /// These registers belong to the called function. | ||
| 144 | pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | ||
| 145 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | ||
| 146 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | ||
| 147 | |||
| 148 | /// Encoding helper functions for x86_64 instructions | ||
| 149 | /// | ||
| 150 | /// Many of these helpers do very little, but they can help make things | ||
| 151 | /// slightly more readable with more descriptive field names / function names. | ||
| 152 | /// | ||
| 153 | /// Some of them also have asserts to ensure that we aren't doing dumb things. | ||
| 154 | /// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, | ||
| 155 | /// you need to encode it with an SIB byte. | ||
| 156 | /// | ||
| 157 | /// Note that ALL of these helper functions will assume capacity, | ||
| 158 | /// so ensure that the `code` has sufficient capacity before using them. | ||
| 159 | /// The `init` method is the recommended way to ensure capacity. | ||
| 160 | pub const Encoder = struct { | ||
| 161 | /// Non-owning reference to the code array | ||
| 162 | code: *ArrayList(u8), | ||
| 163 | |||
| 164 | const Self = @This(); | ||
| 165 | |||
| 166 | /// Wrap `code` in Encoder to make it easier to call these helper functions | ||
| 167 | /// | ||
| 168 | /// maximum_inst_size should contain the maximum number of bytes | ||
| 169 | /// that the encoded instruction will take. | ||
| 170 | /// This is because the helper functions will assume capacity | ||
| 171 | /// in order to avoid bounds checking. | ||
| 172 | pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self { | ||
| 173 | try code.ensureUnusedCapacity(maximum_inst_size); | ||
| 174 | return Self{ .code = code }; | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Directly write a number to the code array with big endianness | ||
| 178 | pub fn writeIntBig(self: Self, comptime T: type, value: T) void { | ||
| 179 | mem.writeIntBig( | ||
| 180 | T, | ||
| 181 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | ||
| 182 | value, | ||
| 183 | ); | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Directly write a number to the code array with little endianness | ||
| 187 | pub fn writeIntLittle(self: Self, comptime T: type, value: T) void { | ||
| 188 | mem.writeIntLittle( | ||
| 189 | T, | ||
| 190 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | ||
| 191 | value, | ||
| 192 | ); | ||
| 193 | } | ||
| 194 | |||
| 195 | // -------- | ||
| 196 | // Prefixes | ||
| 197 | // -------- | ||
| 198 | |||
| 199 | pub const LegacyPrefixes = packed struct { | ||
| 200 | /// LOCK | ||
| 201 | prefix_f0: bool = false, | ||
| 202 | /// REPNZ, REPNE, REP, Scalar Double-precision | ||
| 203 | prefix_f2: bool = false, | ||
| 204 | /// REPZ, REPE, REP, Scalar Single-precision | ||
| 205 | prefix_f3: bool = false, | ||
| 206 | |||
| 207 | /// CS segment override or Branch not taken | ||
| 208 | prefix_2e: bool = false, | ||
| 209 | /// DS segment override | ||
| 210 | prefix_36: bool = false, | ||
| 211 | /// ES segment override | ||
| 212 | prefix_26: bool = false, | ||
| 213 | /// FS segment override | ||
| 214 | prefix_64: bool = false, | ||
| 215 | /// GS segment override | ||
| 216 | prefix_65: bool = false, | ||
| 217 | |||
| 218 | /// Branch taken | ||
| 219 | prefix_3e: bool = false, | ||
| 220 | |||
| 221 | /// Operand size override (enables 16 bit operation) | ||
| 222 | prefix_66: bool = false, | ||
| 223 | |||
| 224 | /// Address size override (enables 16 bit address size) | ||
| 225 | prefix_67: bool = false, | ||
| 226 | |||
| 227 | padding: u5 = 0, | ||
| 228 | }; | ||
| 229 | |||
| 230 | /// Encodes legacy prefixes | ||
| 231 | pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { | ||
| 232 | if (@bitCast(u16, prefixes) != 0) { | ||
| 233 | // Hopefully this path isn't taken very often, so we'll do it the slow way for now | ||
| 234 | |||
| 235 | // LOCK | ||
| 236 | if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); | ||
| 237 | // REPNZ, REPNE, REP, Scalar Double-precision | ||
| 238 | if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); | ||
| 239 | // REPZ, REPE, REP, Scalar Single-precision | ||
| 240 | if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); | ||
| 241 | |||
| 242 | // CS segment override or Branch not taken | ||
| 243 | if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); | ||
| 244 | // DS segment override | ||
| 245 | if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); | ||
| 246 | // ES segment override | ||
| 247 | if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); | ||
| 248 | // FS segment override | ||
| 249 | if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); | ||
| 250 | // GS segment override | ||
| 251 | if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); | ||
| 252 | |||
| 253 | // Branch taken | ||
| 254 | if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); | ||
| 255 | |||
| 256 | // Operand size override | ||
| 257 | if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); | ||
| 258 | |||
| 259 | // Address size override | ||
| 260 | if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Use 16 bit operand size | ||
| 265 | /// | ||
| 266 | /// Note that this flag is overridden by REX.W, if both are present. | ||
| 267 | pub fn prefix16BitMode(self: Self) void { | ||
| 268 | self.code.appendAssumeCapacity(0x66); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | ||
| 272 | pub const Rex = struct { | ||
| 273 | /// Wide, enables 64-bit operation | ||
| 274 | w: bool = false, | ||
| 275 | /// Extends the reg field in the ModR/M byte | ||
| 276 | r: bool = false, | ||
| 277 | /// Extends the index field in the SIB byte | ||
| 278 | x: bool = false, | ||
| 279 | /// Extends the r/m field in the ModR/M byte, | ||
| 280 | /// or the base field in the SIB byte, | ||
| 281 | /// or the reg field in the Opcode byte | ||
| 282 | b: bool = false, | ||
| 283 | }; | ||
| 284 | |||
| 285 | /// Encodes a REX prefix byte given all the fields | ||
| 286 | /// | ||
| 287 | /// Use this byte whenever you need 64 bit operation, | ||
| 288 | /// or one of reg, index, r/m, base, or opcode-reg might be extended. | ||
| 289 | /// | ||
| 290 | /// See struct `Rex` for a description of each field. | ||
| 291 | /// | ||
| 292 | /// Does not add a prefix byte if none of the fields are set! | ||
| 293 | pub fn rex(self: Self, byte: Rex) void { | ||
| 294 | var value: u8 = 0b0100_0000; | ||
| 295 | |||
| 296 | if (byte.w) value |= 0b1000; | ||
| 297 | if (byte.r) value |= 0b0100; | ||
| 298 | if (byte.x) value |= 0b0010; | ||
| 299 | if (byte.b) value |= 0b0001; | ||
| 300 | |||
| 301 | if (value != 0b0100_0000) { | ||
| 302 | self.code.appendAssumeCapacity(value); | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | // ------ | ||
| 307 | // Opcode | ||
| 308 | // ------ | ||
| 309 | |||
| 310 | /// Encodes a 1 byte opcode | ||
| 311 | pub fn opcode_1byte(self: Self, opcode: u8) void { | ||
| 312 | self.code.appendAssumeCapacity(opcode); | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Encodes a 2 byte opcode | ||
| 316 | /// | ||
| 317 | /// e.g. IMUL has the opcode 0x0f 0xaf, so you use | ||
| 318 | /// | ||
| 319 | /// encoder.opcode_2byte(0x0f, 0xaf); | ||
| 320 | pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { | ||
| 321 | self.code.appendAssumeCapacity(prefix); | ||
| 322 | self.code.appendAssumeCapacity(opcode); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Encodes a 1 byte opcode with a reg field | ||
| 326 | /// | ||
| 327 | /// Remember to add a REX prefix byte if reg is extended! | ||
| 328 | pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { | ||
| 329 | assert(opcode & 0b111 == 0); | ||
| 330 | self.code.appendAssumeCapacity(opcode | reg); | ||
| 331 | } | ||
| 332 | |||
| 333 | // ------ | ||
| 334 | // ModR/M | ||
| 335 | // ------ | ||
| 336 | |||
| 337 | /// Construct a ModR/M byte given all the fields | ||
| 338 | /// | ||
| 339 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 340 | pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { | ||
| 341 | self.code.appendAssumeCapacity( | ||
| 342 | @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, | ||
| 343 | ); | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Construct a ModR/M byte using direct r/m addressing | ||
| 347 | /// r/m effective address: r/m | ||
| 348 | /// | ||
| 349 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 350 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 351 | pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 352 | self.modRm(0b11, reg_or_opx, rm); | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Construct a ModR/M byte using indirect r/m addressing | ||
| 356 | /// r/m effective address: [r/m] | ||
| 357 | /// | ||
| 358 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 359 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 360 | pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 361 | assert(rm != 4 and rm != 5); | ||
| 362 | self.modRm(0b00, reg_or_opx, rm); | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Construct a ModR/M byte using indirect SIB addressing | ||
| 366 | /// r/m effective address: [SIB] | ||
| 367 | /// | ||
| 368 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 369 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 370 | pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { | ||
| 371 | self.modRm(0b00, reg_or_opx, 0b100); | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Construct a ModR/M byte using RIP-relative addressing | ||
| 375 | /// r/m effective address: [RIP + disp32] | ||
| 376 | /// | ||
| 377 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 378 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 379 | pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { | ||
| 380 | self.modRm(0b00, reg_or_opx, 0b101); | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Construct a ModR/M byte using indirect r/m with a 8bit displacement | ||
| 384 | /// r/m effective address: [r/m + disp8] | ||
| 385 | /// | ||
| 386 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 387 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 388 | pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 389 | assert(rm != 4); | ||
| 390 | self.modRm(0b01, reg_or_opx, rm); | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Construct a ModR/M byte using indirect SIB with a 8bit displacement | ||
| 394 | /// r/m effective address: [SIB + disp8] | ||
| 395 | /// | ||
| 396 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 397 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 398 | pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { | ||
| 399 | self.modRm(0b01, reg_or_opx, 0b100); | ||
| 400 | } | ||
| 401 | |||
| 402 | /// Construct a ModR/M byte using indirect r/m with a 32bit displacement | ||
| 403 | /// r/m effective address: [r/m + disp32] | ||
| 404 | /// | ||
| 405 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 406 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 407 | pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { | ||
| 408 | assert(rm != 4); | ||
| 409 | self.modRm(0b10, reg_or_opx, rm); | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Construct a ModR/M byte using indirect SIB with a 32bit displacement | ||
| 413 | /// r/m effective address: [SIB + disp32] | ||
| 414 | /// | ||
| 415 | /// Note reg's effective address is always just reg for the ModR/M byte. | ||
| 416 | /// Remember to add a REX prefix byte if reg or rm are extended! | ||
| 417 | pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { | ||
| 418 | self.modRm(0b10, reg_or_opx, 0b100); | ||
| 419 | } | ||
| 420 | |||
| 421 | // --- | ||
| 422 | // SIB | ||
| 423 | // --- | ||
| 424 | |||
| 425 | /// Construct a SIB byte given all the fields | ||
| 426 | /// | ||
| 427 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 428 | pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 429 | self.code.appendAssumeCapacity( | ||
| 430 | @as(u8, scale) << 6 | @as(u8, index) << 3 | base, | ||
| 431 | ); | ||
| 432 | } | ||
| 433 | |||
| 434 | /// Construct a SIB byte with scale * index + base, no frills. | ||
| 435 | /// r/m effective address: [base + scale * index] | ||
| 436 | /// | ||
| 437 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 438 | pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 439 | assert(base != 5); | ||
| 440 | |||
| 441 | self.sib(scale, index, base); | ||
| 442 | } | ||
| 443 | |||
| 444 | /// Construct a SIB byte with scale * index + disp32 | ||
| 445 | /// r/m effective address: [scale * index + disp32] | ||
| 446 | /// | ||
| 447 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 448 | pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { | ||
| 449 | assert(index != 4); | ||
| 450 | |||
| 451 | // scale is actually ignored | ||
| 452 | // index = 4 means no index | ||
| 453 | // base = 5 means no base, if mod == 0. | ||
| 454 | self.sib(scale, index, 5); | ||
| 455 | } | ||
| 456 | |||
| 457 | /// Construct a SIB byte with just base | ||
| 458 | /// r/m effective address: [base] | ||
| 459 | /// | ||
| 460 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 461 | pub fn sib_base(self: Self, base: u3) void { | ||
| 462 | assert(base != 5); | ||
| 463 | |||
| 464 | // scale is actually ignored | ||
| 465 | // index = 4 means no index | ||
| 466 | self.sib(0, 4, base); | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Construct a SIB byte with just disp32 | ||
| 470 | /// r/m effective address: [disp32] | ||
| 471 | /// | ||
| 472 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 473 | pub fn sib_disp32(self: Self) void { | ||
| 474 | // scale is actually ignored | ||
| 475 | // index = 4 means no index | ||
| 476 | // base = 5 means no base, if mod == 0. | ||
| 477 | self.sib(0, 4, 5); | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Construct a SIB byte with scale * index + base + disp8 | ||
| 481 | /// r/m effective address: [base + scale * index + disp8] | ||
| 482 | /// | ||
| 483 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 484 | pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 485 | self.sib(scale, index, base); | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Construct a SIB byte with base + disp8, no index | ||
| 489 | /// r/m effective address: [base + disp8] | ||
| 490 | /// | ||
| 491 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 492 | pub fn sib_baseDisp8(self: Self, base: u3) void { | ||
| 493 | // scale is ignored | ||
| 494 | // index = 4 means no index | ||
| 495 | self.sib(0, 4, base); | ||
| 496 | } | ||
| 497 | |||
| 498 | /// Construct a SIB byte with scale * index + base + disp32 | ||
| 499 | /// r/m effective address: [base + scale * index + disp32] | ||
| 500 | /// | ||
| 501 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 502 | pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { | ||
| 503 | self.sib(scale, index, base); | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Construct a SIB byte with base + disp32, no index | ||
| 507 | /// r/m effective address: [base + disp32] | ||
| 508 | /// | ||
| 509 | /// Remember to add a REX prefix byte if index or base are extended! | ||
| 510 | pub fn sib_baseDisp32(self: Self, base: u3) void { | ||
| 511 | // scale is ignored | ||
| 512 | // index = 4 means no index | ||
| 513 | self.sib(0, 4, base); | ||
| 514 | } | ||
| 515 | |||
| 516 | // ------------------------- | ||
| 517 | // Trivial (no bit fiddling) | ||
| 518 | // ------------------------- | ||
| 519 | |||
| 520 | /// Encode an 8 bit immediate | ||
| 521 | /// | ||
| 522 | /// It is sign-extended to 64 bits by the cpu. | ||
| 523 | pub fn imm8(self: Self, imm: i8) void { | ||
| 524 | self.code.appendAssumeCapacity(@bitCast(u8, imm)); | ||
| 525 | } | ||
| 526 | |||
| 527 | /// Encode an 8 bit displacement | ||
| 528 | /// | ||
| 529 | /// It is sign-extended to 64 bits by the cpu. | ||
| 530 | pub fn disp8(self: Self, disp: i8) void { | ||
| 531 | self.code.appendAssumeCapacity(@bitCast(u8, disp)); | ||
| 532 | } | ||
| 533 | |||
| 534 | /// Encode an 16 bit immediate | ||
| 535 | /// | ||
| 536 | /// It is sign-extended to 64 bits by the cpu. | ||
| 537 | pub fn imm16(self: Self, imm: i16) void { | ||
| 538 | self.writeIntLittle(i16, imm); | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Encode an 32 bit immediate | ||
| 542 | /// | ||
| 543 | /// It is sign-extended to 64 bits by the cpu. | ||
| 544 | pub fn imm32(self: Self, imm: i32) void { | ||
| 545 | self.writeIntLittle(i32, imm); | ||
| 546 | } | ||
| 547 | |||
| 548 | /// Encode an 32 bit displacement | ||
| 549 | /// | ||
| 550 | /// It is sign-extended to 64 bits by the cpu. | ||
| 551 | pub fn disp32(self: Self, disp: i32) void { | ||
| 552 | self.writeIntLittle(i32, disp); | ||
| 553 | } | ||
| 554 | |||
| 555 | /// Encode an 64 bit immediate | ||
| 556 | /// | ||
| 557 | /// It is sign-extended to 64 bits by the cpu. | ||
| 558 | pub fn imm64(self: Self, imm: u64) void { | ||
| 559 | self.writeIntLittle(u64, imm); | ||
| 560 | } | ||
| 561 | }; | ||
| 562 | |||
| 563 | test "x86_64 Encoder helpers" { | ||
| 564 | var code = ArrayList(u8).init(testing.allocator); | ||
| 565 | defer code.deinit(); | ||
| 566 | |||
| 567 | // simple integer multiplication | ||
| 568 | |||
| 569 | // imul eax,edi | ||
| 570 | // 0faf c7 | ||
| 571 | { | ||
| 572 | try code.resize(0); | ||
| 573 | const encoder = try Encoder.init(&code, 4); | ||
| 574 | encoder.rex(.{ | ||
| 575 | .r = Register.eax.isExtended(), | ||
| 576 | .b = Register.edi.isExtended(), | ||
| 577 | }); | ||
| 578 | encoder.opcode_2byte(0x0f, 0xaf); | ||
| 579 | encoder.modRm_direct( | ||
| 580 | Register.eax.low_id(), | ||
| 581 | Register.edi.low_id(), | ||
| 582 | ); | ||
| 583 | |||
| 584 | try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); | ||
| 585 | } | ||
| 586 | |||
| 587 | // simple mov | ||
| 588 | |||
| 589 | // mov eax,edi | ||
| 590 | // 89 f8 | ||
| 591 | { | ||
| 592 | try code.resize(0); | ||
| 593 | const encoder = try Encoder.init(&code, 3); | ||
| 594 | encoder.rex(.{ | ||
| 595 | .r = Register.edi.isExtended(), | ||
| 596 | .b = Register.eax.isExtended(), | ||
| 597 | }); | ||
| 598 | encoder.opcode_1byte(0x89); | ||
| 599 | encoder.modRm_direct( | ||
| 600 | Register.edi.low_id(), | ||
| 601 | Register.eax.low_id(), | ||
| 602 | ); | ||
| 603 | |||
| 604 | try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); | ||
| 605 | } | ||
| 606 | |||
| 607 | // signed integer addition of 32-bit sign extended immediate to 64 bit register | ||
| 608 | |||
| 609 | // add rcx, 2147483647 | ||
| 610 | // | ||
| 611 | // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding | ||
| 612 | // | ||
| 613 | // 48 : REX.W set for 64 bit operand (*r*cx) | ||
| 614 | // 81 : opcode for "<arithmetic> with immediate" | ||
| 615 | // c1 : id = rcx, | ||
| 616 | // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) | ||
| 617 | // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD | ||
| 618 | // : 001 <-- 001 is rcx | ||
| 619 | // ffffff7f : 2147483647 | ||
| 620 | { | ||
| 621 | try code.resize(0); | ||
| 622 | const encoder = try Encoder.init(&code, 7); | ||
| 623 | encoder.rex(.{ .w = true }); // use 64 bit operation | ||
| 624 | encoder.opcode_1byte(0x81); | ||
| 625 | encoder.modRm_direct( | ||
| 626 | 0, | ||
| 627 | Register.rcx.low_id(), | ||
| 628 | ); | ||
| 629 | encoder.imm32(2147483647); | ||
| 630 | |||
| 631 | try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | // TODO add these registers to the enum and populate dwarfLocOp | ||
| 636 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | ||
| 637 | // RA = (16, "RA"), | ||
| 638 | // | ||
| 639 | // XMM0 = (17, "xmm0"), | ||
| 640 | // XMM1 = (18, "xmm1"), | ||
| 641 | // XMM2 = (19, "xmm2"), | ||
| 642 | // XMM3 = (20, "xmm3"), | ||
| 643 | // XMM4 = (21, "xmm4"), | ||
| 644 | // XMM5 = (22, "xmm5"), | ||
| 645 | // XMM6 = (23, "xmm6"), | ||
| 646 | // XMM7 = (24, "xmm7"), | ||
| 647 | // | ||
| 648 | // XMM8 = (25, "xmm8"), | ||
| 649 | // XMM9 = (26, "xmm9"), | ||
| 650 | // XMM10 = (27, "xmm10"), | ||
| 651 | // XMM11 = (28, "xmm11"), | ||
| 652 | // XMM12 = (29, "xmm12"), | ||
| 653 | // XMM13 = (30, "xmm13"), | ||
| 654 | // XMM14 = (31, "xmm14"), | ||
| 655 | // XMM15 = (32, "xmm15"), | ||
| 656 | // | ||
| 657 | // ST0 = (33, "st0"), | ||
| 658 | // ST1 = (34, "st1"), | ||
| 659 | // ST2 = (35, "st2"), | ||
| 660 | // ST3 = (36, "st3"), | ||
| 661 | // ST4 = (37, "st4"), | ||
| 662 | // ST5 = (38, "st5"), | ||
| 663 | // ST6 = (39, "st6"), | ||
| 664 | // ST7 = (40, "st7"), | ||
| 665 | // | ||
| 666 | // MM0 = (41, "mm0"), | ||
| 667 | // MM1 = (42, "mm1"), | ||
| 668 | // MM2 = (43, "mm2"), | ||
| 669 | // MM3 = (44, "mm3"), | ||
| 670 | // MM4 = (45, "mm4"), | ||
| 671 | // MM5 = (46, "mm5"), | ||
| 672 | // MM6 = (47, "mm6"), | ||
| 673 | // MM7 = (48, "mm7"), | ||
| 674 | // | ||
| 675 | // RFLAGS = (49, "rFLAGS"), | ||
| 676 | // ES = (50, "es"), | ||
| 677 | // CS = (51, "cs"), | ||
| 678 | // SS = (52, "ss"), | ||
| 679 | // DS = (53, "ds"), | ||
| 680 | // FS = (54, "fs"), | ||
| 681 | // GS = (55, "gs"), | ||
| 682 | // | ||
| 683 | // FS_BASE = (58, "fs.base"), | ||
| 684 | // GS_BASE = (59, "gs.base"), | ||
| 685 | // | ||
| 686 | // TR = (62, "tr"), | ||
| 687 | // LDTR = (63, "ldtr"), | ||
| 688 | // MXCSR = (64, "mxcsr"), | ||
| 689 | // FCW = (65, "fcw"), | ||
| 690 | // FSW = (66, "fsw"), | ||
| 691 | // | ||
| 692 | // XMM16 = (67, "xmm16"), | ||
| 693 | // XMM17 = (68, "xmm17"), | ||
| 694 | // XMM18 = (69, "xmm18"), | ||
| 695 | // XMM19 = (70, "xmm19"), | ||
| 696 | // XMM20 = (71, "xmm20"), | ||
| 697 | // XMM21 = (72, "xmm21"), | ||
| 698 | // XMM22 = (73, "xmm22"), | ||
| 699 | // XMM23 = (74, "xmm23"), | ||
| 700 | // XMM24 = (75, "xmm24"), | ||
| 701 | // XMM25 = (76, "xmm25"), | ||
| 702 | // XMM26 = (77, "xmm26"), | ||
| 703 | // XMM27 = (78, "xmm27"), | ||
| 704 | // XMM28 = (79, "xmm28"), | ||
| 705 | // XMM29 = (80, "xmm29"), | ||
| 706 | // XMM30 = (81, "xmm30"), | ||
| 707 | // XMM31 = (82, "xmm31"), | ||
| 708 | // | ||
| 709 | // K0 = (118, "k0"), | ||
| 710 | // K1 = (119, "k1"), | ||
| 711 | // K2 = (120, "k2"), | ||
| 712 | // K3 = (121, "k3"), | ||
| 713 | // K4 = (122, "k4"), | ||
| 714 | // K5 = (123, "k5"), | ||
| 715 | // K6 = (124, "k6"), | ||
| 716 | // K7 = (125, "k7"), | ||
src/link/MachO.zig+2-2| ... | @@ -12,7 +12,7 @@ const math = std.math; | ... | @@ -12,7 +12,7 @@ const math = std.math; |
| 12 | const mem = std.mem; | 12 | const mem = std.mem; |
| 13 | const meta = std.meta; | 13 | const meta = std.meta; |
| 14 | 14 | ||
| 15 | const aarch64 = @import("../codegen/aarch64.zig"); | 15 | const aarch64 = @import("../arch/aarch64/bits.zig"); |
| 16 | const bind = @import("MachO/bind.zig"); | 16 | const bind = @import("MachO/bind.zig"); |
| 17 | const codegen = @import("../codegen.zig"); | 17 | const codegen = @import("../codegen.zig"); |
| 18 | const commands = @import("MachO/commands.zig"); | 18 | const commands = @import("MachO/commands.zig"); |
| ... | @@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, | ... | @@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, |
| 200 | 200 | ||
| 201 | /// List of atoms that are owned directly by the linker. | 201 | /// List of atoms that are owned directly by the linker. |
| 202 | /// Currently these are only atoms that are the result of linking | 202 | /// Currently these are only atoms that are the result of linking |
| 203 | /// object files. Atoms which take part in incremental linking are | 203 | /// object files. Atoms which take part in incremental linking are |
| 204 | /// at present owned by Module.Decl. | 204 | /// at present owned by Module.Decl. |
| 205 | /// TODO consolidate this. | 205 | /// TODO consolidate this. |
| 206 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | 206 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
src/link/MachO/Atom.zig+1-1| ... | @@ -2,7 +2,7 @@ const Atom = @This(); | ... | @@ -2,7 +2,7 @@ const Atom = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | 4 | const build_options = @import("build_options"); |
| 5 | const aarch64 = @import("../../codegen/aarch64.zig"); | 5 | const aarch64 = @import("../../arch/aarch64/bits.zig"); |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const commands = @import("commands.zig"); | 7 | const commands = @import("commands.zig"); |
| 8 | const log = std.log.scoped(.text_block); | 8 | const log = std.log.scoped(.text_block); |