| 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 | 551 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 552 | 552 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 553 | 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 | 558 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 555 | 559 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 556 | 560 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| 557 | 561 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" |
| 558 | "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig" | |
| 559 | "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig" | |
| 560 | 562 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" |
| 561 | 563 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" |
| 562 | 564 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" |
| 563 | "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig" | |
| 564 | 565 | "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig" |
| 565 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" | |
| 566 | 566 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 567 | 567 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 568 | 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 | 21 | const build_options = @import("build_options"); |
| 22 | 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 | 26 | pub const FnResult = union(enum) { |
| 27 | 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 | 470 | /// A branch in the ARM instruction set |
| 471 | 471 | arm_branch: struct { |
| 472 | 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 | 5336 | } |
| 5337 | 5337 | |
| 5338 | 5338 | const Register = switch (arch) { |
| 5339 | .i386 => @import("codegen/x86.zig").Register, | |
| 5340 | .x86_64 => @import("codegen/x86_64.zig").Register, | |
| 5341 | .riscv64 => @import("codegen/riscv64.zig").Register, | |
| 5342 | .arm, .armeb => @import("codegen/arm.zig").Register, | |
| 5343 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register, | |
| 5339 | .i386 => @import("arch/x86/bits.zig").Register, | |
| 5340 | .x86_64 => @import("arch/x86_64/bits.zig").Register, | |
| 5341 | .riscv64 => @import("arch/riscv64/bits.zig").Register, | |
| 5342 | .arm, .armeb => @import("arch/arm/bits.zig").Register, | |
| 5343 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register, | |
| 5344 | 5344 | else => enum { |
| 5345 | 5345 | dummy, |
| 5346 | 5346 | |
| ... | ... | @@ -5352,39 +5352,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 5352 | 5352 | }; |
| 5353 | 5353 | |
| 5354 | 5354 | const Instruction = switch (arch) { |
| 5355 | .riscv64 => @import("codegen/riscv64.zig").Instruction, | |
| 5356 | .arm, .armeb => @import("codegen/arm.zig").Instruction, | |
| 5357 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction, | |
| 5355 | .riscv64 => @import("arch/riscv64/bits.zig").Instruction, | |
| 5356 | .arm, .armeb => @import("arch/arm/bits.zig").Instruction, | |
| 5357 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction, | |
| 5358 | 5358 | else => void, |
| 5359 | 5359 | }; |
| 5360 | 5360 | |
| 5361 | 5361 | const Condition = switch (arch) { |
| 5362 | .arm, .armeb => @import("codegen/arm.zig").Condition, | |
| 5362 | .arm, .armeb => @import("arch/arm/bits.zig").Condition, | |
| 5363 | 5363 | else => void, |
| 5364 | 5364 | }; |
| 5365 | 5365 | |
| 5366 | 5366 | const callee_preserved_regs = switch (arch) { |
| 5367 | .i386 => @import("codegen/x86.zig").callee_preserved_regs, | |
| 5368 | .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs, | |
| 5369 | .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs, | |
| 5370 | .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs, | |
| 5371 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs, | |
| 5367 | .i386 => @import("arch/x86/bits.zig").callee_preserved_regs, | |
| 5368 | .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs, | |
| 5369 | .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs, | |
| 5370 | .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs, | |
| 5371 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs, | |
| 5372 | 5372 | else => [_]Register{}, |
| 5373 | 5373 | }; |
| 5374 | 5374 | |
| 5375 | 5375 | const c_abi_int_param_regs = switch (arch) { |
| 5376 | .i386 => @import("codegen/x86.zig").c_abi_int_param_regs, | |
| 5377 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs, | |
| 5378 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs, | |
| 5379 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs, | |
| 5376 | .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs, | |
| 5377 | .x86_64 => @import("arch/x86_64/bits.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("arch/aarch64/bits.zig").c_abi_int_param_regs, | |
| 5380 | 5380 | else => [_]Register{}, |
| 5381 | 5381 | }; |
| 5382 | 5382 | |
| 5383 | 5383 | const c_abi_int_return_regs = switch (arch) { |
| 5384 | .i386 => @import("codegen/x86.zig").c_abi_int_return_regs, | |
| 5385 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs, | |
| 5386 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs, | |
| 5387 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs, | |
| 5384 | .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs, | |
| 5385 | .x86_64 => @import("arch/x86_64/bits.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("arch/aarch64/bits.zig").c_abi_int_return_regs, | |
| 5388 | 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 | 12 | const mem = std.mem; |
| 13 | 13 | const meta = std.meta; |
| 14 | 14 | |
| 15 | const aarch64 = @import("../codegen/aarch64.zig"); | |
| 15 | const aarch64 = @import("../arch/aarch64/bits.zig"); | |
| 16 | 16 | const bind = @import("MachO/bind.zig"); |
| 17 | 17 | const codegen = @import("../codegen.zig"); |
| 18 | 18 | const commands = @import("MachO/commands.zig"); |
| ... | ... | @@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, |
| 200 | 200 | |
| 201 | 201 | /// List of atoms that are owned directly by the linker. |
| 202 | 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 | 204 | /// at present owned by Module.Decl. |
| 205 | 205 | /// TODO consolidate this. |
| 206 | 206 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
src/link/MachO/Atom.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const Atom = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | 4 | const build_options = @import("build_options"); |
| 5 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 5 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const commands = @import("commands.zig"); |
| 8 | 8 | const log = std.log.scoped(.text_block); |