| ... | ... | @@ -544,6 +544,12 @@ const Encoding = enum { |
| 544 | 544 | |
| 545 | 545 | /// OP r64, imm64 |
| 546 | 546 | oi, |
| 547 | |
| 548 | /// OP al/ax/eax/rax, moffs |
| 549 | fd, |
| 550 | |
| 551 | /// OP moffs, al/ax/eax/rax |
| 552 | td, |
| 547 | 553 | }; |
| 548 | 554 | |
| 549 | 555 | inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 { |
| ... | ... | @@ -581,6 +587,14 @@ inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 { |
| 581 | 587 | .mov => 0xb8, |
| 582 | 588 | else => unreachable, |
| 583 | 589 | }, |
| 590 | .fd => return switch (tag) { |
| 591 | .mov => 0xa1, |
| 592 | else => unreachable, |
| 593 | }, |
| 594 | .td => return switch (tag) { |
| 595 | .mov => 0xa3, |
| 596 | else => unreachable, |
| 597 | }, |
| 584 | 598 | } |
| 585 | 599 | } |
| 586 | 600 | |
| ... | ... | @@ -629,6 +643,65 @@ const RegisterOrMemory = union(enum) { |
| 629 | 643 | } |
| 630 | 644 | }; |
| 631 | 645 | |
| 646 | fn lowerToTdEnc( |
| 647 | tag: Mir.Inst.Tag, |
| 648 | moffs: i64, |
| 649 | reg: Register, |
| 650 | code: *std.ArrayList(u8), |
| 651 | ) InnerError!void { |
| 652 | return lowerToTdFdEnc(tag, reg, moffs, code, true); |
| 653 | } |
| 654 | |
| 655 | fn lowerToFdEnc( |
| 656 | tag: Mir.Inst.Tag, |
| 657 | reg: Register, |
| 658 | moffs: i64, |
| 659 | code: *std.ArrayList(u8), |
| 660 | ) InnerError!void { |
| 661 | return lowerToTdFdEnc(tag, reg, moffs, code, false); |
| 662 | } |
| 663 | |
| 664 | fn lowerToTdFdEnc( |
| 665 | tag: Mir.Inst.Tag, |
| 666 | reg: Register, |
| 667 | moffs: i64, |
| 668 | code: *std.ArrayList(u8), |
| 669 | td: bool, |
| 670 | ) InnerError!void { |
| 671 | if (reg.lowId() != Register.rax.lowId()) return error.EmitFail; |
| 672 | if (reg.size() != immOpSize(moffs)) return error.EmitFail; |
| 673 | var opc = if (td) getOpCode(tag, .td) else getOpCode(tag, .fd); |
| 674 | if (reg.size() == 8) { |
| 675 | opc -= 1; |
| 676 | } |
| 677 | const encoder = try Encoder.init(code, 10); |
| 678 | if (reg.size() == 16) { |
| 679 | encoder.opcode_1byte(0x66); |
| 680 | } |
| 681 | encoder.rex(.{ |
| 682 | .w = reg.size() == 64, |
| 683 | }); |
| 684 | encoder.opcode_1byte(opc); |
| 685 | switch (reg.size()) { |
| 686 | 8 => { |
| 687 | const moffs8 = try math.cast(i8, moffs); |
| 688 | encoder.imm8(moffs8); |
| 689 | }, |
| 690 | 16 => { |
| 691 | const moffs16 = try math.cast(i16, moffs); |
| 692 | encoder.imm16(moffs16); |
| 693 | }, |
| 694 | 32 => { |
| 695 | const moffs32 = try math.cast(i32, moffs); |
| 696 | encoder.imm32(moffs32); |
| 697 | }, |
| 698 | 64 => { |
| 699 | encoder.imm64(@bitCast(u64, moffs)); |
| 700 | }, |
| 701 | else => unreachable, |
| 702 | } |
| 703 | } |
| 704 | |
| 632 | 705 | fn lowerToOiEnc( |
| 633 | 706 | tag: Mir.Inst.Tag, |
| 634 | 707 | reg: Register, |
| ... | ... | @@ -641,6 +714,9 @@ fn lowerToOiEnc( |
| 641 | 714 | opc -= 8; |
| 642 | 715 | } |
| 643 | 716 | const encoder = try Encoder.init(code, 10); |
| 717 | if (reg.size() == 16) { |
| 718 | encoder.opcode_1byte(0x66); |
| 719 | } |
| 644 | 720 | encoder.rex(.{ |
| 645 | 721 | .w = reg.size() == 64, |
| 646 | 722 | .b = reg.isExtended(), |
| ... | ... | @@ -1065,52 +1141,24 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1065 | 1141 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 1066 | 1142 | assert(tag == .movabs); |
| 1067 | 1143 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1068 | | |
| 1144 | const imm: i64 = if (ops.reg1.size() == 64) blk: { |
| 1145 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 1146 | const imm = emit.mir.extraData(Mir.Imm64, payload).data; |
| 1147 | break :blk @bitCast(i64, imm.decode()); |
| 1148 | } else emit.mir.instructions.items(.data)[inst].imm; |
| 1069 | 1149 | if (ops.flags == 0b00) { |
| 1070 | 1150 | // movabs reg, imm64 |
| 1071 | 1151 | // OI |
| 1072 | | const imm: i64 = if (ops.reg1.size() == 64) blk: { |
| 1073 | | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 1074 | | const imm = emit.mir.extraData(Mir.Imm64, payload).data; |
| 1075 | | break :blk @bitCast(i64, imm.decode()); |
| 1076 | | } else emit.mir.instructions.items(.data)[inst].imm; |
| 1077 | 1152 | return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); |
| 1078 | 1153 | } |
| 1079 | | |
| 1080 | | const encoder = try Encoder.init(emit.code, 10); |
| 1081 | | const is_64 = blk: { |
| 1082 | | if (ops.reg1 == .none) { |
| 1083 | | // movabs moffs64, rax |
| 1084 | | const opc: u8 = if (ops.reg2.size() == 8) 0xa2 else 0xa3; |
| 1085 | | encoder.rex(.{ |
| 1086 | | .w = ops.reg2.size() == 64, |
| 1087 | | }); |
| 1088 | | encoder.opcode_1byte(opc); |
| 1089 | | break :blk ops.reg2.size() == 64; |
| 1090 | | } else { |
| 1091 | | // movabs rax, moffs64 |
| 1092 | | const opc: u8 = if (ops.reg2.size() == 8) 0xa0 else 0xa1; |
| 1093 | | encoder.rex(.{ |
| 1094 | | .w = ops.reg1.size() == 64, |
| 1095 | | }); |
| 1096 | | encoder.opcode_1byte(opc); |
| 1097 | | break :blk ops.reg1.size() == 64; |
| 1098 | | } |
| 1099 | | }; |
| 1100 | | |
| 1101 | | if (is_64) { |
| 1102 | | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 1103 | | const imm64 = emit.mir.extraData(Mir.Imm64, payload).data; |
| 1104 | | encoder.imm64(imm64.decode()); |
| 1154 | if (ops.reg1 == .none) { |
| 1155 | // movabs moffs64, rax |
| 1156 | // TD |
| 1157 | return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); |
| 1105 | 1158 | } else { |
| 1106 | | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 1107 | | if (imm <= math.maxInt(i8)) { |
| 1108 | | encoder.imm8(@intCast(i8, imm)); |
| 1109 | | } else if (imm <= math.maxInt(i16)) { |
| 1110 | | encoder.imm16(@intCast(i16, imm)); |
| 1111 | | } else { |
| 1112 | | encoder.imm32(imm); |
| 1113 | | } |
| 1159 | // movabs rax, moffs64 |
| 1160 | // FD |
| 1161 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); |
| 1114 | 1162 | } |
| 1115 | 1163 | } |
| 1116 | 1164 | |
| ... | ... | @@ -1563,6 +1611,25 @@ test "lower OI encoding" { |
| 1563 | 1611 | ); |
| 1564 | 1612 | try lowerToOiEnc(.mov, .r11d, 0x10000000, code.buffer()); |
| 1565 | 1613 | try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", code.emitted(), "mov r11d, 0x10000000"); |
| 1614 | try lowerToOiEnc(.mov, .r11w, 0x1000, code.buffer()); |
| 1615 | try expectEqualHexStrings("\x66\x41\xBB\x00\x10", code.emitted(), "mov r11w, 0x1000"); |
| 1566 | 1616 | try lowerToOiEnc(.mov, .r11b, 0x10, code.buffer()); |
| 1567 | 1617 | try expectEqualHexStrings("\x41\xB3\x10", code.emitted(), "mov r11b, 0x10"); |
| 1568 | 1618 | } |
| 1619 | |
| 1620 | test "lower FD/TD encoding" { |
| 1621 | var code = TestEmitCode.init(); |
| 1622 | defer code.deinit(); |
| 1623 | try lowerToFdEnc(.mov, .rax, 0x1000000000000000, code.buffer()); |
| 1624 | try expectEqualHexStrings( |
| 1625 | "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10", |
| 1626 | code.emitted(), |
| 1627 | "mov rax, ds:0x1000000000000000", |
| 1628 | ); |
| 1629 | try lowerToFdEnc(.mov, .eax, 0x10000000, code.buffer()); |
| 1630 | try expectEqualHexStrings("\xa1\x00\x00\x00\x10", code.emitted(), "mov eax, ds:0x10000000"); |
| 1631 | try lowerToFdEnc(.mov, .ax, 0x1000, code.buffer()); |
| 1632 | try expectEqualHexStrings("\x66\xa1\x00\x10", code.emitted(), "mov ax, ds:0x1000"); |
| 1633 | try lowerToFdEnc(.mov, .al, 0x10, code.buffer()); |
| 1634 | try expectEqualHexStrings("\xa0\x10", code.emitted(), "mov al, ds:0x10"); |
| 1635 | } |