| ... | @@ -376,6 +376,34 @@ const Function = struct { | ... | @@ -376,6 +376,34 @@ const Function = struct { |
| 376 | } | 376 | } |
| 377 | } | 377 | } |
| 378 | | 378 | |
| | 379 | /// Encodes a REX prefix as specified, and appends it to the instruction |
| | 380 | /// stream. This only modifies the instruction stream if at least one bit |
| | 381 | /// is set true, which has a few implications: |
| | 382 | /// |
| | 383 | /// * The length of the instruction buffer will be modified *if* the |
| | 384 | /// resulting REX is meaningful, but will remain the same if it is not. |
| | 385 | /// * Deliberately inserting a "meaningless REX" requires explicit usage of |
| | 386 | /// 0x40, and cannot be done via this function. |
| | 387 | fn REX(self: *Function, arg: struct { B: bool = false, W: bool = false, X: bool = false, R: bool = false }) !void { |
| | 388 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. |
| | 389 | var value: u8 = 0x40; |
| | 390 | if (arg.B) { |
| | 391 | value |= 0x1; |
| | 392 | } |
| | 393 | if (arg.X) { |
| | 394 | value |= 0x2; |
| | 395 | } |
| | 396 | if (arg.R) { |
| | 397 | value |= 0x4; |
| | 398 | } |
| | 399 | if (arg.W) { |
| | 400 | value |= 0x8; |
| | 401 | } |
| | 402 | if (value != 0x40) { |
| | 403 | try self.code.append(value); |
| | 404 | } |
| | 405 | } |
| | 406 | |
| 379 | fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void { | 407 | fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void { |
| 380 | switch (arch) { | 408 | switch (arch) { |
| 381 | .x86_64 => switch (mcv) { | 409 | .x86_64 => switch (mcv) { |
| ... | @@ -395,24 +423,14 @@ const Function = struct { | ... | @@ -395,24 +423,14 @@ const Function = struct { |
| 395 | // then three bits for the operand. Since we're zeroing a register, the two three-bit | 423 | // then three bits for the operand. Since we're zeroing a register, the two three-bit |
| 396 | // values will be identical, and the mode is three (the raw register value). | 424 | // values will be identical, and the mode is three (the raw register value). |
| 397 | // | 425 | // |
| 398 | if (reg.isExtended()) { | 426 | // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since |
| 399 | // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since | 427 | // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB. |
| 400 | // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB. | 428 | // Both R and B are set, as we're extending, in effect, the register bits *and* the operand. |
| 401 | // Both R and B are set, as we're extending, in effect, the register bits *and* the operand. | 429 | try self.REX(.{ .R = reg.isExtended(), .B = reg.isExtended() }); |
| 402 | // | 430 | const id = @as(u8, reg.id() & 0b111); |
| 403 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. In this case, that's | 431 | return self.code.appendSlice(&[_]u8{ |
| 404 | // b01000101, or 0x45. | 432 | 0x31, 0xC0 | id << 3 | id, |
| 405 | return self.code.appendSlice(&[_]u8{ | 433 | }); |
| 406 | 0x45, | | |
| 407 | 0x31, | | |
| 408 | 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()), | | |
| 409 | }); | | |
| 410 | } else { | | |
| 411 | return self.code.appendSlice(&[_]u8{ | | |
| 412 | 0x31, | | |
| 413 | 0xC0 | (@as(u8, reg.id()) << 3) | reg.id(), | | |
| 414 | }); | | |
| 415 | } | | |
| 416 | } | 434 | } |
| 417 | if (x <= std.math.maxInt(u32)) { | 435 | if (x <= std.math.maxInt(u32)) { |
| 418 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. | 436 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. |
| ... | @@ -445,9 +463,9 @@ const Function = struct { | ... | @@ -445,9 +463,9 @@ const Function = struct { |
| 445 | // Since we always need a REX here, let's just check if we also need to set REX.B. | 463 | // Since we always need a REX here, let's just check if we also need to set REX.B. |
| 446 | // | 464 | // |
| 447 | // In this case, the encoding of the REX byte is 0b0100100B | 465 | // In this case, the encoding of the REX byte is 0b0100100B |
| 448 | const REX = 0x48 | (if (reg.isExtended()) @as(u8, 0x01) else 0); | 466 | |
| 449 | try self.code.resize(self.code.items.len + 10); | 467 | try self.REX(.{ .W = true, .B = reg.isExtended() }); |
| 450 | self.code.items[self.code.items.len - 10] = REX; | 468 | try self.code.resize(self.code.items.len + 9); |
| 451 | self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111); | 469 | self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111); |
| 452 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | 470 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; |
| 453 | mem.writeIntLittle(u64, imm_ptr, x); | 471 | mem.writeIntLittle(u64, imm_ptr, x); |
| ... | @@ -463,12 +481,11 @@ const Function = struct { | ... | @@ -463,12 +481,11 @@ const Function = struct { |
| 463 | // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three | 481 | // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three |
| 464 | // bits as five. | 482 | // bits as five. |
| 465 | // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id. | 483 | // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id. |
| 466 | try self.code.resize(self.code.items.len + 7); | 484 | try self.REX(.{ .W = true, .B = reg.isExtended() }); |
| 467 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0; | 485 | try self.code.resize(self.code.items.len + 6); |
| 468 | const rip = self.code.items.len; | 486 | const rip = self.code.items.len; |
| 469 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); | 487 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); |
| 470 | const offset = @intCast(i32, big_offset); | 488 | const offset = @intCast(i32, big_offset); |
| 471 | self.code.items[self.code.items.len - 7] = REX; | | |
| 472 | self.code.items[self.code.items.len - 6] = 0x8D; | 489 | self.code.items[self.code.items.len - 6] = 0x8D; |
| 473 | self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3); | 490 | self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3); |
| 474 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 491 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; |
| ... | @@ -485,9 +502,9 @@ const Function = struct { | ... | @@ -485,9 +502,9 @@ const Function = struct { |
| 485 | // If the *source* is extended, the B field must be 1. | 502 | // If the *source* is extended, the B field must be 1. |
| 486 | // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle | 503 | // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle |
| 487 | // three bits) contain the destination, and the R/M field (the lower three bits) contain the source. | 504 | // three bits) contain the destination, and the R/M field (the lower three bits) contain the source. |
| 488 | const REX = 0x48 | (if (reg.isExtended()) @as(u8, 4) else 0) | (if (src_reg.isExtended()) @as(u8, 1) else 0); | 505 | try self.REX(.{ .W = true, .R = reg.isExtended(), .B = src_reg.isExtended() }); |
| 489 | const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, src_reg.id()); | 506 | const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111); |
| 490 | try self.code.appendSlice(&[_]u8{ REX, 0x8B, R }); | 507 | try self.code.appendSlice(&[_]u8{ 0x8B, R }); |
| 491 | }, | 508 | }, |
| 492 | .memory => |x| { | 509 | .memory => |x| { |
| 493 | if (reg.size() != 64) { | 510 | if (reg.size() != 64) { |
| ... | @@ -501,10 +518,9 @@ const Function = struct { | ... | @@ -501,10 +518,9 @@ const Function = struct { |
| 501 | // The SIB must be 0x25, to indicate a disp32 with no scaled index. | 518 | // The SIB must be 0x25, to indicate a disp32 with no scaled index. |
| 502 | // 0b00RRR100, where RRR is the lower three bits of the register ID. | 519 | // 0b00RRR100, where RRR is the lower three bits of the register ID. |
| 503 | // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32. | 520 | // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32. |
| 504 | try self.code.resize(self.code.items.len + 8); | 521 | try self.REX(.{ .W = true, .B = reg.isExtended() }); |
| 505 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 1) else 0; | 522 | try self.code.resize(self.code.items.len + 7); |
| 506 | const r = 0x04 | (@as(u8, reg.id() & 0b111) << 3); | 523 | const r = 0x04 | (@as(u8, reg.id() & 0b111) << 3); |
| 507 | self.code.items[self.code.items.len - 8] = REX; | | |
| 508 | self.code.items[self.code.items.len - 7] = 0x8B; | 524 | self.code.items[self.code.items.len - 7] = 0x8B; |
| 509 | self.code.items[self.code.items.len - 6] = r; | 525 | self.code.items[self.code.items.len - 6] = r; |
| 510 | self.code.items[self.code.items.len - 5] = 0x25; | 526 | self.code.items[self.code.items.len - 5] = 0x25; |
| ... | @@ -546,9 +562,9 @@ const Function = struct { | ... | @@ -546,9 +562,9 @@ const Function = struct { |
| 546 | // | 562 | // |
| 547 | // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both* | 563 | // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both* |
| 548 | // register operands need to be marked as extended. | 564 | // register operands need to be marked as extended. |
| 549 | const REX = 0x48 | if (reg.isExtended()) @as(u8, 0b0101) else 0; | 565 | try self.REX(.{ .W = true, .B = reg.isExtended(), .R = reg.isExtended() }); |
| 550 | const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()); | 566 | const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()); |
| 551 | try self.code.appendSlice(&[_]u8{ REX, 0x8B, RM }); | 567 | try self.code.appendSlice(&[_]u8{ 0x8B, RM }); |
| 552 | } | 568 | } |
| 553 | } | 569 | } |
| 554 | }, | 570 | }, |