| ... | @@ -11,6 +11,8 @@ const ErrorMsg = Module.ErrorMsg; | ... | @@ -11,6 +11,8 @@ const ErrorMsg = Module.ErrorMsg; |
| 11 | const Target = std.Target; | 11 | const Target = std.Target; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | | 13 | |
| | 14 | const Backend = @import("backend.zig"); |
| | 15 | |
| 14 | pub const Result = union(enum) { | 16 | pub const Result = union(enum) { |
| 15 | /// The `code` parameter passed to `generateSymbol` has the value appended. | 17 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| 16 | appended: void, | 18 | appended: void, |
| ... | @@ -348,172 +350,182 @@ const Function = struct { | ... | @@ -348,172 +350,182 @@ const Function = struct { |
| 348 | } | 350 | } |
| 349 | } | 351 | } |
| 350 | | 352 | |
| 351 | fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) !void { | 353 | fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void { |
| 352 | switch (arch) { | 354 | switch (arch) { |
| 353 | .x86_64 => switch (reg) { | 355 | .x86_64 => switch (mcv) { |
| 354 | .rax => switch (mcv) { | 356 | .none, .unreach => unreachable, |
| 355 | .none, .unreach => unreachable, | 357 | .immediate => |x| { |
| 356 | .immediate => |x| { | 358 | if (reg.size() != 64) { |
| 357 | // Setting the eax register zeroes the upper part of rax, so if the number is small | 359 | return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{}); |
| 358 | // enough, that is preferable. | 360 | } |
| 359 | // Best case: zero | 361 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit |
| 360 | // 31 c0 xor eax,eax | 362 | // register is the fastest way to zero a register. |
| 361 | if (x == 0) { | 363 | if (x == 0) { |
| 362 | return self.code.appendSlice(&[_]u8{ 0x31, 0xc0 }); | 364 | // The encoding for `xor r32, r32` is `0x31 /r`. |
| | 365 | // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the |
| | 366 | // ModR/M byte of the instruction contains a register operand and an r/m operand." |
| | 367 | // |
| | 368 | // R/M bytes are composed of two bits for the mode, then three bits for the register, |
| | 369 | // then three bits for the operand. Since we're zeroing a register, the two three-bit |
| | 370 | // values will be identical, and the mode is three (the raw register value). |
| | 371 | // |
| | 372 | if (reg.isExtended()) { |
| | 373 | // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since |
| | 374 | // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB. |
| | 375 | // Both R and B are set, as we're extending, in effect, the register bits *and* the operand. |
| | 376 | // |
| | 377 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. In this case, that's |
| | 378 | // b01000101, or 0x45. |
| | 379 | return self.code.appendSlice(&[_]u8{ |
| | 380 | 0x45, |
| | 381 | 0x31, |
| | 382 | 0xC0 | (@intCast(u8, @truncate(u3, reg.id())) << 3) | @truncate(u3, reg.id()), |
| | 383 | }); |
| | 384 | } else { |
| | 385 | return self.code.appendSlice(&[_]u8{ |
| | 386 | 0x31, |
| | 387 | 0xC0 | (@intCast(u8, reg.id()) << 3) | @intCast(u3, reg.id()), |
| | 388 | }); |
| 363 | } | 389 | } |
| 364 | // Next best case: set eax with 4 bytes | 390 | } |
| 365 | // b8 04 03 02 01 mov eax,0x01020304 | 391 | if (x <= std.math.maxInt(u32)) { |
| 366 | if (x <= std.math.maxInt(u32)) { | 392 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. |
| | 393 | // |
| | 394 | // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM. |
| | 395 | if (reg.isExtended()) { |
| | 396 | // Just as with XORing, we need a REX prefix. This time though, we only |
| | 397 | // need the B bit set, as we're extending the opcode's register field, |
| | 398 | // and there is no Mod R/M byte. |
| | 399 | // |
| | 400 | // Thus, we need b01000001, or 0x41. |
| | 401 | try self.code.resize(self.code.items.len + 6); |
| | 402 | self.code.items[self.code.items.len - 6] = 0x41; |
| | 403 | } else { |
| 367 | try self.code.resize(self.code.items.len + 5); | 404 | try self.code.resize(self.code.items.len + 5); |
| 368 | self.code.items[self.code.items.len - 5] = 0xb8; | | |
| 369 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | | |
| 370 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | | |
| 371 | return; | | |
| 372 | } | 405 | } |
| 373 | // Worst case: set rax with 8 bytes | 406 | self.code.items[self.code.items.len - 5] = 0xB8 | @intCast(u8, @truncate(u3, reg.id())); |
| 374 | // 48 b8 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708 | 407 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; |
| 375 | try self.code.resize(self.code.items.len + 10); | 408 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); |
| 376 | self.code.items[self.code.items.len - 10] = 0x48; | | |
| 377 | self.code.items[self.code.items.len - 9] = 0xb8; | | |
| 378 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | | |
| 379 | mem.writeIntLittle(u64, imm_ptr, x); | | |
| 380 | return; | 409 | return; |
| 381 | }, | 410 | } |
| 382 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}), | 411 | // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls |
| 383 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rax = register", .{}), | 412 | // this `movabs`, though this is officially just a different variant of the plain `mov` |
| 384 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rax = memory", .{}), | 413 | // instruction. |
| | 414 | // |
| | 415 | // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only |
| | 416 | // difference is that we set REX.W before the instruction, which extends the load to |
| | 417 | // 64-bit and uses the full bit-width of the register. |
| | 418 | // |
| | 419 | // Since we always need a REX here, let's just check if we also need to set REX.B. |
| | 420 | // |
| | 421 | // In this case, the encoding of the REX byte is 0b0100100B |
| | 422 | const REX = 0x48 | (if (reg.isExtended()) @as(u8, 0x01) else 0); |
| | 423 | try self.code.resize(self.code.items.len + 10); |
| | 424 | self.code.items[self.code.items.len - 10] = REX; |
| | 425 | self.code.items[self.code.items.len - 9] = 0xB8 | @intCast(u8, @truncate(u3, reg.id())); |
| | 426 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; |
| | 427 | mem.writeIntLittle(u64, imm_ptr, x); |
| 385 | }, | 428 | }, |
| 386 | .rdx => switch (mcv) { | 429 | .embedded_in_code => |code_offset| { |
| 387 | .none, .unreach => unreachable, | 430 | if (reg.size() != 64) { |
| 388 | .immediate => |x| { | 431 | return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{}); |
| 389 | // Setting the edx register zeroes the upper part of rdx, so if the number is small | 432 | } |
| 390 | // enough, that is preferable. | 433 | // We need the offset from RIP in a signed i32 twos complement. |
| 391 | // Best case: zero | 434 | // The instruction is 7 bytes long and RIP points to the next instruction. |
| 392 | // 31 d2 xor edx,edx | 435 | // |
| 393 | if (x == 0) { | 436 | // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified, |
| 394 | return self.code.appendSlice(&[_]u8{ 0x31, 0xd2 }); | 437 | // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three |
| 395 | } | 438 | // bits as five. |
| 396 | // Next best case: set edx with 4 bytes | 439 | // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id. |
| 397 | // ba 04 03 02 01 mov edx,0x1020304 | 440 | try self.code.resize(self.code.items.len + 7); |
| 398 | if (x <= std.math.maxInt(u32)) { | 441 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0; |
| 399 | try self.code.resize(self.code.items.len + 5); | 442 | const rip = self.code.items.len; |
| 400 | self.code.items[self.code.items.len - 5] = 0xba; | 443 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); |
| 401 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 444 | const offset = @intCast(i32, big_offset); |
| 402 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | 445 | self.code.items[self.code.items.len - 7] = REX; |
| 403 | return; | 446 | self.code.items[self.code.items.len - 6] = 0x8D; |
| 404 | } | 447 | self.code.items[self.code.items.len - 5] = 0x5 | (@intCast(u8, @truncate(u3, reg.id())) << 3); |
| 405 | // Worst case: set rdx with 8 bytes | 448 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; |
| 406 | // 48 ba 08 07 06 05 04 03 02 01 movabs rdx,0x0102030405060708 | 449 | mem.writeIntLittle(i32, imm_ptr, offset); |
| 407 | try self.code.resize(self.code.items.len + 10); | | |
| 408 | self.code.items[self.code.items.len - 10] = 0x48; | | |
| 409 | self.code.items[self.code.items.len - 9] = 0xba; | | |
| 410 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | | |
| 411 | mem.writeIntLittle(u64, imm_ptr, x); | | |
| 412 | return; | | |
| 413 | }, | | |
| 414 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}), | | |
| 415 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = register", .{}), | | |
| 416 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = memory", .{}), | | |
| 417 | }, | 450 | }, |
| 418 | .rdi => switch (mcv) { | 451 | .register => |r| { |
| 419 | .none, .unreach => unreachable, | 452 | if (reg.size() != 64) { |
| 420 | .immediate => |x| { | 453 | return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{}); |
| 421 | // Setting the edi register zeroes the upper part of rdi, so if the number is small | 454 | } |
| 422 | // enough, that is preferable. | 455 | const src_reg = @intToEnum(Reg(arch), @intCast(u8, r)); |
| 423 | // Best case: zero | 456 | // This is a varient of 8B /r. Since we're using 64-bit moves, we require a REX. |
| 424 | // 31 ff xor edi,edi | 457 | // This is thus three bytes: REX 0x8B R/M. |
| 425 | if (x == 0) { | 458 | // If the destination is extended, the R field must be 1. |
| 426 | return self.code.appendSlice(&[_]u8{ 0x31, 0xff }); | 459 | // If the *source* is extended, the B field must be 1. |
| 427 | } | 460 | // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle |
| 428 | // Next best case: set edi with 4 bytes | 461 | // three bits) contain the destination, and the R/M field (the lower three bits) contain the source. |
| 429 | // bf 04 03 02 01 mov edi,0x1020304 | 462 | const REX = 0x48 | (if (reg.isExtended()) @as(u8, 4) else 0) | (if (src_reg.isExtended()) @as(u8, 1) else 0); |
| 430 | if (x <= std.math.maxInt(u32)) { | 463 | const R = 0xC0 | (@intCast(u8, @truncate(u3, reg.id())) << 3) | @truncate(u3, src_reg.id()); |
| 431 | try self.code.resize(self.code.items.len + 5); | 464 | try self.code.appendSlice(&[_]u8{ REX, 0x8B, R }); |
| 432 | self.code.items[self.code.items.len - 5] = 0xbf; | | |
| 433 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | | |
| 434 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | | |
| 435 | return; | | |
| 436 | } | | |
| 437 | // Worst case: set rdi with 8 bytes | | |
| 438 | // 48 bf 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708 | | |
| 439 | try self.code.resize(self.code.items.len + 10); | | |
| 440 | self.code.items[self.code.items.len - 10] = 0x48; | | |
| 441 | self.code.items[self.code.items.len - 9] = 0xbf; | | |
| 442 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | | |
| 443 | mem.writeIntLittle(u64, imm_ptr, x); | | |
| 444 | return; | | |
| 445 | }, | | |
| 446 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}), | | |
| 447 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = register", .{}), | | |
| 448 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = memory", .{}), | | |
| 449 | }, | 465 | }, |
| 450 | .rsi => switch (mcv) { | 466 | .memory => |x| { |
| 451 | .none, .unreach => unreachable, | 467 | if (reg.size() != 64) { |
| 452 | .immediate => |x| { | 468 | return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{}); |
| 453 | // Setting the edi register zeroes the upper part of rdi, so if the number is small | 469 | } |
| 454 | // enough, that is preferable. | 470 | if (x <= std.math.maxInt(u32)) { |
| 455 | // Best case: zero | 471 | // Moving from memory to a register is a variant of `8B /r`. |
| 456 | // 31 f6 xor esi,esi | 472 | // Since we're using 64-bit moves, we require a REX. |
| 457 | if (x == 0) { | 473 | // This variant also requires a SIB, as it would otherwise be RIP-relative. |
| 458 | return self.code.appendSlice(&[_]u8{ 0x31, 0xf6 }); | 474 | // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement. |
| 459 | } | 475 | // The SIB must be 0x25, to indicate a disp32 with no scaled index. |
| 460 | // Next best case: set esi with 4 bytes | 476 | // 0b00RRR100, where RRR is the lower three bits of the register ID. |
| 461 | // be 40 30 20 10 mov esi,0x10203040 | 477 | // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32. |
| 462 | if (x <= std.math.maxInt(u32)) { | 478 | try self.code.resize(self.code.items.len + 8); |
| 463 | try self.code.resize(self.code.items.len + 5); | 479 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0; |
| 464 | self.code.items[self.code.items.len - 5] = 0xbe; | 480 | const r = 0x04 | (@intCast(u8, @truncate(u3, reg.id())) << 3); |
| 465 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 481 | self.code.items[self.code.items.len - 8] = REX; |
| 466 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | 482 | self.code.items[self.code.items.len - 7] = 0x8B; |
| 467 | return; | 483 | self.code.items[self.code.items.len - 6] = r; |
| 468 | } | 484 | self.code.items[self.code.items.len - 5] = 0x25; |
| 469 | // Worst case: set rsi with 8 bytes | | |
| 470 | // 48 be 80 70 60 50 40 30 20 10 movabs rsi,0x1020304050607080 | | |
| 471 | | | |
| 472 | try self.code.resize(self.code.items.len + 10); | | |
| 473 | self.code.items[self.code.items.len - 10] = 0x48; | | |
| 474 | self.code.items[self.code.items.len - 9] = 0xbe; | | |
| 475 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | | |
| 476 | mem.writeIntLittle(u64, imm_ptr, x); | | |
| 477 | return; | | |
| 478 | }, | | |
| 479 | .embedded_in_code => |code_offset| { | | |
| 480 | // Examples: | | |
| 481 | // lea rsi, [rip + 0x01020304] | | |
| 482 | // lea rsi, [rip - 7] | | |
| 483 | // f: 48 8d 35 04 03 02 01 lea rsi,[rip+0x1020304] # 102031a <_start+0x102031a> | | |
| 484 | // 16: 48 8d 35 f9 ff ff ff lea rsi,[rip+0xfffffffffffffff9] # 16 <_start+0x16> | | |
| 485 | // | | |
| 486 | // We need the offset from RIP in a signed i32 twos complement. | | |
| 487 | // The instruction is 7 bytes long and RIP points to the next instruction. | | |
| 488 | try self.code.resize(self.code.items.len + 7); | | |
| 489 | const rip = self.code.items.len; | | |
| 490 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); | | |
| 491 | const offset = @intCast(i32, big_offset); | | |
| 492 | self.code.items[self.code.items.len - 7] = 0x48; | | |
| 493 | self.code.items[self.code.items.len - 6] = 0x8d; | | |
| 494 | self.code.items[self.code.items.len - 5] = 0x35; | | |
| 495 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 485 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; |
| 496 | mem.writeIntLittle(i32, imm_ptr, offset); | 486 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); |
| 497 | return; | 487 | } else { |
| 498 | }, | 488 | // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load |
| 499 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = register", .{}), | 489 | // the value. |
| 500 | .memory => |x| { | 490 | if (reg.id() == 0) { |
| 501 | if (x <= std.math.maxInt(u32)) { | 491 | // REX.W 0xA1 moffs64* |
| 502 | // 48 8b 34 25 40 30 20 10 mov rsi,QWORD PTR ds:0x10203040 | 492 | // moffs64* is a 64-bit offset "relative to segment base", which really just means the |
| 503 | try self.code.resize(self.code.items.len + 8); | 493 | // absolute address for all practical purposes. |
| 504 | self.code.items[self.code.items.len - 8] = 0x48; | 494 | try self.code.resize(self.code.items.len + 10); |
| 505 | self.code.items[self.code.items.len - 7] = 0x8b; | 495 | // REX.W == 0x48 |
| 506 | self.code.items[self.code.items.len - 6] = 0x34; | 496 | self.code.items[self.code.items.len - 10] = 0x48; |
| 507 | self.code.items[self.code.items.len - 5] = 0x25; | 497 | self.code.items[self.code.items.len - 9] = 0xA1; |
| 508 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 498 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; |
| 509 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | 499 | mem.writeIntLittle(u64, imm_ptr, x); |
| 510 | return; | | |
| 511 | } else { | 500 | } else { |
| 512 | return self.fail(src, "TODO implement genSetReg for x86_64 setting rsi to 64-bit memory", .{}); | 501 | // This requires two instructions; a move imm as used above, followed by an indirect load using the register |
| | 502 | // as the address and the register as the destination. |
| | 503 | // |
| | 504 | // This cannot be used if the lower three bits of the id are equal to four or five, as there |
| | 505 | // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with |
| | 506 | // this instruction. |
| | 507 | const id3 = @truncate(u3, reg.id()); |
| | 508 | std.debug.assert(id3 != 4 and id3 != 5); |
| | 509 | |
| | 510 | // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. |
| | 511 | try self.genSetReg(src, arch, reg, MCValue{ .immediate = x }); |
| | 512 | |
| | 513 | // Now, the register contains the address of the value to load into it |
| | 514 | // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. |
| | 515 | // TODO: determine whether to allow other sized registers, and if so, handle them properly. |
| | 516 | // This operation requires three bytes: REX 0x8B R/M |
| | 517 | // |
| | 518 | // For this operation, we want R/M mode *zero* (use register indirectly), and the two register |
| | 519 | // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID. |
| | 520 | // |
| | 521 | // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both* |
| | 522 | // register operands need to be marked as extended. |
| | 523 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 0b0101) else 0; |
| | 524 | const RM = (@intCast(u8, @truncate(u3, reg.id())) << 3) | @truncate(u3, reg.id()); |
| | 525 | try self.code.appendSlice(&[_]u8{ REX, 0x8B, RM }); |
| 513 | } | 526 | } |
| 514 | }, | 527 | } |
| 515 | }, | 528 | }, |
| 516 | else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}), | | |
| 517 | }, | 529 | }, |
| 518 | else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}), | 530 | else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}), |
| 519 | } | 531 | } |
| ... | @@ -579,113 +591,8 @@ const Function = struct { | ... | @@ -579,113 +591,8 @@ const Function = struct { |
| 579 | | 591 | |
| 580 | fn Reg(comptime arch: Target.Cpu.Arch) type { | 592 | fn Reg(comptime arch: Target.Cpu.Arch) type { |
| 581 | return switch (arch) { | 593 | return switch (arch) { |
| 582 | .i386 => enum { | 594 | .i386 => Backend.x86.Register, |
| 583 | eax, | 595 | .x86_64 => Backend.x86_64.Register, |
| 584 | ebx, | | |
| 585 | ecx, | | |
| 586 | edx, | | |
| 587 | ebp, | | |
| 588 | esp, | | |
| 589 | esi, | | |
| 590 | edi, | | |
| 591 | | | |
| 592 | ax, | | |
| 593 | bx, | | |
| 594 | cx, | | |
| 595 | dx, | | |
| 596 | bp, | | |
| 597 | sp, | | |
| 598 | si, | | |
| 599 | di, | | |
| 600 | | | |
| 601 | ah, | | |
| 602 | bh, | | |
| 603 | ch, | | |
| 604 | dh, | | |
| 605 | | | |
| 606 | al, | | |
| 607 | bl, | | |
| 608 | cl, | | |
| 609 | dl, | | |
| 610 | }, | | |
| 611 | .x86_64 => enum { | | |
| 612 | rax, | | |
| 613 | rbx, | | |
| 614 | rcx, | | |
| 615 | rdx, | | |
| 616 | rbp, | | |
| 617 | rsp, | | |
| 618 | rsi, | | |
| 619 | rdi, | | |
| 620 | r8, | | |
| 621 | r9, | | |
| 622 | r10, | | |
| 623 | r11, | | |
| 624 | r12, | | |
| 625 | r13, | | |
| 626 | r14, | | |
| 627 | r15, | | |
| 628 | | | |
| 629 | eax, | | |
| 630 | ebx, | | |
| 631 | ecx, | | |
| 632 | edx, | | |
| 633 | ebp, | | |
| 634 | esp, | | |
| 635 | esi, | | |
| 636 | edi, | | |
| 637 | r8d, | | |
| 638 | r9d, | | |
| 639 | r10d, | | |
| 640 | r11d, | | |
| 641 | r12d, | | |
| 642 | r13d, | | |
| 643 | r14d, | | |
| 644 | r15d, | | |
| 645 | | | |
| 646 | ax, | | |
| 647 | bx, | | |
| 648 | cx, | | |
| 649 | dx, | | |
| 650 | bp, | | |
| 651 | sp, | | |
| 652 | si, | | |
| 653 | di, | | |
| 654 | r8w, | | |
| 655 | r9w, | | |
| 656 | r10w, | | |
| 657 | r11w, | | |
| 658 | r12w, | | |
| 659 | r13w, | | |
| 660 | r14w, | | |
| 661 | r15w, | | |
| 662 | | | |
| 663 | ah, | | |
| 664 | bh, | | |
| 665 | ch, | | |
| 666 | dh, | | |
| 667 | bph, | | |
| 668 | sph, | | |
| 669 | sih, | | |
| 670 | dih, | | |
| 671 | | | |
| 672 | al, | | |
| 673 | bl, | | |
| 674 | cl, | | |
| 675 | dl, | | |
| 676 | bpl, | | |
| 677 | spl, | | |
| 678 | sil, | | |
| 679 | dil, | | |
| 680 | r8b, | | |
| 681 | r9b, | | |
| 682 | r10b, | | |
| 683 | r11b, | | |
| 684 | r12b, | | |
| 685 | r13b, | | |
| 686 | r14b, | | |
| 687 | r15b, | | |
| 688 | }, | | |
| 689 | else => @compileError("TODO add more register enums"), | 596 | else => @compileError("TODO add more register enums"), |
| 690 | }; | 597 | }; |
| 691 | } | 598 | } |