| ... | @@ -544,6 +544,12 @@ const Encoding = enum { | ... | @@ -544,6 +544,12 @@ const Encoding = enum { |
| 544 | | 544 | |
| 545 | /// OP r64, imm64 | 545 | /// OP r64, imm64 |
| 546 | oi, | 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 | inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 { | 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,6 +587,14 @@ inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 { |
| 581 | .mov => 0xb8, | 587 | .mov => 0xb8, |
| 582 | else => unreachable, | 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,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 | fn lowerToOiEnc( | 705 | fn lowerToOiEnc( |
| 633 | tag: Mir.Inst.Tag, | 706 | tag: Mir.Inst.Tag, |
| 634 | reg: Register, | 707 | reg: Register, |
| ... | @@ -641,6 +714,9 @@ fn lowerToOiEnc( | ... | @@ -641,6 +714,9 @@ fn lowerToOiEnc( |
| 641 | opc -= 8; | 714 | opc -= 8; |
| 642 | } | 715 | } |
| 643 | const encoder = try Encoder.init(code, 10); | 716 | const encoder = try Encoder.init(code, 10); |
| | 717 | if (reg.size() == 16) { |
| | 718 | encoder.opcode_1byte(0x66); |
| | 719 | } |
| 644 | encoder.rex(.{ | 720 | encoder.rex(.{ |
| 645 | .w = reg.size() == 64, | 721 | .w = reg.size() == 64, |
| 646 | .b = reg.isExtended(), | 722 | .b = reg.isExtended(), |
| ... | @@ -1065,52 +1141,24 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1065,52 +1141,24 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1065 | const tag = emit.mir.instructions.items(.tag)[inst]; | 1141 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 1066 | assert(tag == .movabs); | 1142 | assert(tag == .movabs); |
| 1067 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); | 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 | if (ops.flags == 0b00) { | 1149 | if (ops.flags == 0b00) { |
| 1070 | // movabs reg, imm64 | 1150 | // movabs reg, imm64 |
| 1071 | // OI | 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 | return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); | 1152 | return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); |
| 1078 | } | 1153 | } |
| 1079 | | 1154 | if (ops.reg1 == .none) { |
| 1080 | const encoder = try Encoder.init(emit.code, 10); | 1155 | // movabs moffs64, rax |
| 1081 | const is_64 = blk: { | 1156 | // TD |
| 1082 | if (ops.reg1 == .none) { | 1157 | return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); |
| 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()); | | |
| 1105 | } else { | 1158 | } else { |
| 1106 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 1159 | // movabs rax, moffs64 |
| 1107 | if (imm <= math.maxInt(i8)) { | 1160 | // FD |
| 1108 | encoder.imm8(@intCast(i8, imm)); | 1161 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); |
| 1109 | } else if (imm <= math.maxInt(i16)) { | | |
| 1110 | encoder.imm16(@intCast(i16, imm)); | | |
| 1111 | } else { | | |
| 1112 | encoder.imm32(imm); | | |
| 1113 | } | | |
| 1114 | } | 1162 | } |
| 1115 | } | 1163 | } |
| 1116 | | 1164 | |
| ... | @@ -1563,6 +1611,25 @@ test "lower OI encoding" { | ... | @@ -1563,6 +1611,25 @@ test "lower OI encoding" { |
| 1563 | ); | 1611 | ); |
| 1564 | try lowerToOiEnc(.mov, .r11d, 0x10000000, code.buffer()); | 1612 | try lowerToOiEnc(.mov, .r11d, 0x10000000, code.buffer()); |
| 1565 | try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", code.emitted(), "mov r11d, 0x10000000"); | 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 | try lowerToOiEnc(.mov, .r11b, 0x10, code.buffer()); | 1616 | try lowerToOiEnc(.mov, .r11b, 0x10, code.buffer()); |
| 1567 | try expectEqualHexStrings("\x41\xB3\x10", code.emitted(), "mov r11b, 0x10"); | 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 | } |