authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-22 18:53:20+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-22 19:01:43+01:00
logb9a6f81d1ac0e04381b5872413f49ff54b872e04
tree68289c1cbd22b50ed2c57d7c728ebfdb2b2745bb
parent2b5de9403d51561642b241f5940d5e7630993849

stage2: add lowering fn for OI encoding

Implement movabs using OI generic encoding.

1 files changed, 163 insertions(+), 83 deletions(-)

src/arch/x86_64/Emit.zig+163-83
......@@ -541,61 +541,64 @@ const Encoding = enum {
541541
542542 /// OP r64, r/m64
543543 rm,
544};
545544
546const OpCode = struct {
547 opc: u8,
548 /// Only used if `Encoding == .mi`.
549 modrm_ext: u3,
545 /// OP r64, imm64
546 oi,
550547};
551548
552inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) OpCode {
549inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 {
553550 switch (enc) {
554551 .mi => return switch (tag) {
555 .adc => .{ .opc = 0x81, .modrm_ext = 0x2 },
556 .add => .{ .opc = 0x81, .modrm_ext = 0x0 },
557 .sub => .{ .opc = 0x81, .modrm_ext = 0x5 },
558 .xor => .{ .opc = 0x81, .modrm_ext = 0x6 },
559 .@"and" => .{ .opc = 0x81, .modrm_ext = 0x4 },
560 .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },
561 .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },
562 .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },
563 .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 },
552 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81,
553 .mov => 0xc7,
564554 else => unreachable,
565555 },
566 .mr => {
567 const opc: u8 = switch (tag) {
568 .adc => 0x11,
569 .add => 0x01,
570 .sub => 0x29,
571 .xor => 0x31,
572 .@"and" => 0x21,
573 .@"or" => 0x09,
574 .sbb => 0x19,
575 .cmp => 0x39,
576 .mov => 0x89,
577 else => unreachable,
578 };
579 return .{ .opc = opc, .modrm_ext = undefined };
556 .mr => return switch (tag) {
557 .adc => 0x11,
558 .add => 0x01,
559 .sub => 0x29,
560 .xor => 0x31,
561 .@"and" => 0x21,
562 .@"or" => 0x09,
563 .sbb => 0x19,
564 .cmp => 0x39,
565 .mov => 0x89,
566 else => unreachable,
580567 },
581 .rm => {
582 const opc: u8 = switch (tag) {
583 .adc => 0x13,
584 .add => 0x03,
585 .sub => 0x2b,
586 .xor => 0x33,
587 .@"and" => 0x23,
588 .@"or" => 0x0b,
589 .sbb => 0x1b,
590 .cmp => 0x3b,
591 .mov => 0x8b,
592 else => unreachable,
593 };
594 return .{ .opc = opc, .modrm_ext = undefined };
568 .rm => return switch (tag) {
569 .adc => 0x13,
570 .add => 0x03,
571 .sub => 0x2b,
572 .xor => 0x33,
573 .@"and" => 0x23,
574 .@"or" => 0x0b,
575 .sbb => 0x1b,
576 .cmp => 0x3b,
577 .mov => 0x8b,
578 else => unreachable,
579 },
580 .oi => return switch (tag) {
581 .mov => 0xb8,
582 else => unreachable,
595583 },
596584 }
597585}
598586
587inline fn getMiModRmExt(tag: Mir.Inst.Tag) u3 {
588 return switch (tag) {
589 .adc => 0x2,
590 .add => 0x0,
591 .sub => 0x5,
592 .xor => 0x6,
593 .@"and" => 0x4,
594 .@"or" => 0x1,
595 .sbb => 0x3,
596 .cmp => 0x7,
597 .mov => 0x0,
598 else => unreachable,
599 };
600}
601
599602const ScaleIndexBase = struct {
600603 scale: u2,
601604 index_reg: ?Register,
......@@ -626,16 +629,56 @@ const RegisterOrMemory = union(enum) {
626629 }
627630};
628631
632fn lowerToOiEnc(
633 tag: Mir.Inst.Tag,
634 reg: Register,
635 imm: i64,
636 code: *std.ArrayList(u8),
637) InnerError!void {
638 var opc = getOpCode(tag, .oi);
639 if (reg.size() != immOpSize(imm)) return error.EmitFail;
640 if (reg.size() == 8) {
641 opc -= 8;
642 }
643 const encoder = try Encoder.init(code, 10);
644 encoder.rex(.{
645 .w = reg.size() == 64,
646 .b = reg.isExtended(),
647 });
648 encoder.opcode_withReg(opc, reg.lowId());
649 switch (reg.size()) {
650 8 => {
651 const imm8 = try math.cast(i8, imm);
652 encoder.imm8(imm8);
653 },
654 16 => {
655 const imm16 = try math.cast(i16, imm);
656 encoder.imm16(imm16);
657 },
658 32 => {
659 const imm32 = try math.cast(i32, imm);
660 encoder.imm32(imm32);
661 },
662 64 => {
663 encoder.imm64(@bitCast(u64, imm));
664 },
665 else => unreachable,
666 }
667}
668
629669fn lowerToMiEnc(
630670 tag: Mir.Inst.Tag,
631671 reg_or_mem: RegisterOrMemory,
632672 imm: i32,
633673 code: *std.ArrayList(u8),
634674) InnerError!void {
635 const opcode = getOpCode(tag, .mi);
675 var opc = getOpCode(tag, .mi);
676 const modrm_ext = getMiModRmExt(tag);
636677 switch (reg_or_mem) {
637678 .register => |dst_reg| {
638 const opc: u8 = if (dst_reg.size() == 8) opcode.opc - 1 else opcode.opc;
679 if (dst_reg.size() == 8) {
680 opc -= 1;
681 }
639682 const encoder = try Encoder.init(code, 7);
640683 if (dst_reg.size() == 16) {
641684 // 0x66 prefix switches to the non-default size; here we assume a switch from
......@@ -648,7 +691,7 @@ fn lowerToMiEnc(
648691 .b = dst_reg.isExtended(),
649692 });
650693 encoder.opcode_1byte(opc);
651 encoder.modRm_direct(opcode.modrm_ext, dst_reg.lowId());
694 encoder.modRm_direct(modrm_ext, dst_reg.lowId());
652695 switch (dst_reg.size()) {
653696 8 => {
654697 const imm8 = try math.cast(i8, imm);
......@@ -676,25 +719,25 @@ fn lowerToMiEnc(
676719 .w = false,
677720 .b = dst_reg.isExtended(),
678721 });
679 encoder.opcode_1byte(opcode.opc);
722 encoder.opcode_1byte(opc);
680723 if (dst_mem.disp == 0) {
681 encoder.modRm_indirectDisp0(opcode.modrm_ext, dst_reg.lowId());
724 encoder.modRm_indirectDisp0(modrm_ext, dst_reg.lowId());
682725 } else if (immOpSize(dst_mem.disp) == 8) {
683 encoder.modRm_indirectDisp8(opcode.modrm_ext, dst_reg.lowId());
726 encoder.modRm_indirectDisp8(modrm_ext, dst_reg.lowId());
684727 encoder.disp8(@intCast(i8, dst_mem.disp));
685728 } else {
686729 if (dst_reg.lowId() == 4) {
687 encoder.modRm_SIBDisp32(opcode.modrm_ext);
730 encoder.modRm_SIBDisp32(modrm_ext);
688731 encoder.sib_baseDisp32(dst_reg.lowId());
689732 encoder.disp32(dst_mem.disp);
690733 } else {
691 encoder.modRm_indirectDisp32(opcode.modrm_ext, dst_reg.lowId());
734 encoder.modRm_indirectDisp32(modrm_ext, dst_reg.lowId());
692735 encoder.disp32(dst_mem.disp);
693736 }
694737 }
695738 } else {
696 encoder.opcode_1byte(opcode.opc);
697 encoder.modRm_SIBDisp0(opcode.modrm_ext);
739 encoder.opcode_1byte(opc);
740 encoder.modRm_SIBDisp0(modrm_ext);
698741 encoder.sib_disp32();
699742 encoder.disp32(dst_mem.disp);
700743 }
......@@ -709,8 +752,10 @@ fn lowerToRmEnc(
709752 reg_or_mem: RegisterOrMemory,
710753 code: *std.ArrayList(u8),
711754) InnerError!void {
712 const opcode = getOpCode(tag, .rm);
713 const opc: u8 = if (reg.size() == 8) opcode.opc - 1 else opcode.opc;
755 var opc = getOpCode(tag, .rm);
756 if (reg.size() == 8) {
757 opc -= 1;
758 }
714759 switch (reg_or_mem) {
715760 .register => |src_reg| {
716761 if (reg.size() != src_reg.size()) return error.EmitFail;
......@@ -779,8 +824,10 @@ fn lowerToMrEnc(
779824 // * reg is 32bit - dword ptr
780825 // * reg is 16bit - word ptr
781826 // * reg is 8bit - byte ptr
782 const opcode = getOpCode(tag, .mr);
783 const opc: u8 = if (reg.size() == 8) opcode.opc - 1 else opcode.opc;
827 var opc = getOpCode(tag, .mr);
828 if (reg.size() == 8) {
829 opc -= 1;
830 }
784831 switch (reg_or_mem) {
785832 .register => |dst_reg| {
786833 if (dst_reg.size() != reg.size()) return error.EmitFail;
......@@ -890,7 +937,7 @@ fn mirArith(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!voi
890937 }
891938}
892939
893fn immOpSize(imm: i32) u8 {
940fn immOpSize(imm: i64) u8 {
894941 blk: {
895942 _ = math.cast(i8, imm) catch break :blk;
896943 return 8;
......@@ -899,15 +946,21 @@ fn immOpSize(imm: i32) u8 {
899946 _ = math.cast(i16, imm) catch break :blk;
900947 return 16;
901948 }
902 return 32;
949 blk: {
950 _ = math.cast(i32, imm) catch break :blk;
951 return 32;
952 }
953 return 64;
903954}
904955
905956fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
906957 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
907958 const scale = ops.flags;
908959 // OP reg1, [reg2 + scale*rcx + imm32]
909 const opcode = getOpCode(tag, .rm);
910 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
960 var opc = getOpCode(tag, .rm);
961 if (ops.reg1.size() == 8) {
962 opc -= 1;
963 }
911964 const imm = emit.mir.instructions.items(.data)[inst].imm;
912965 const encoder = try Encoder.init(emit.code, 8);
913966 encoder.rex(.{
......@@ -934,15 +987,18 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
934987
935988 if (ops.reg2 == .none) {
936989 // OP [reg1 + scale*rax + 0], imm32
937 const opcode = getOpCode(tag, .mi);
938 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
990 var opc = getOpCode(tag, .mi);
991 const modrm_ext = getMiModRmExt(tag);
992 if (ops.reg1.size() == 8) {
993 opc -= 1;
994 }
939995 const encoder = try Encoder.init(emit.code, 8);
940996 encoder.rex(.{
941997 .w = ops.reg1.size() == 64,
942998 .b = ops.reg1.isExtended(),
943999 });
9441000 encoder.opcode_1byte(opc);
945 encoder.modRm_SIBDisp0(opcode.modrm_ext);
1001 encoder.modRm_SIBDisp0(modrm_ext);
9461002 encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId());
9471003 if (imm <= math.maxInt(i8)) {
9481004 encoder.imm8(@intCast(i8, imm));
......@@ -955,8 +1011,10 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
9551011 }
9561012
9571013 // OP [reg1 + scale*rax + imm32], reg2
958 const opcode = getOpCode(tag, .mr);
959 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1014 var opc = getOpCode(tag, .mr);
1015 if (ops.reg1.size() == 8) {
1016 opc -= 1;
1017 }
9601018 const encoder = try Encoder.init(emit.code, 8);
9611019 encoder.rex(.{
9621020 .w = ops.reg1.size() == 64,
......@@ -980,8 +1038,11 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
9801038 const scale = ops.flags;
9811039 const payload = emit.mir.instructions.items(.data)[inst].payload;
9821040 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
983 const opcode = getOpCode(tag, .mi);
984 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1041 var opc = getOpCode(tag, .mi);
1042 if (ops.reg1.size() == 8) {
1043 opc -= 1;
1044 }
1045 const modrm_ext = getMiModRmExt(tag);
9851046 const encoder = try Encoder.init(emit.code, 2);
9861047 encoder.rex(.{
9871048 .w = ops.reg1.size() == 64,
......@@ -989,11 +1050,11 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
9891050 });
9901051 encoder.opcode_1byte(opc);
9911052 if (imm_pair.dest_off <= math.maxInt(i8)) {
992 encoder.modRm_SIBDisp8(opcode.modrm_ext);
1053 encoder.modRm_SIBDisp8(modrm_ext);
9931054 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
9941055 encoder.disp8(@intCast(i8, imm_pair.dest_off));
9951056 } else {
996 encoder.modRm_SIBDisp32(opcode.modrm_ext);
1057 encoder.modRm_SIBDisp32(modrm_ext);
9971058 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
9981059 encoder.disp32(imm_pair.dest_off);
9991060 }
......@@ -1005,21 +1066,19 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10051066 assert(tag == .movabs);
10061067 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
10071068
1069 if (ops.flags == 0b00) {
1070 // movabs reg, imm64
1071 // 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);
1078 }
1079
10081080 const encoder = try Encoder.init(emit.code, 10);
10091081 const is_64 = blk: {
1010 if (ops.flags == 0b00) {
1011 // movabs reg, imm64
1012 const opc: u8 = if (ops.reg1.size() == 8) 0xb0 else 0xb8;
1013 if (ops.reg1.size() == 64) {
1014 encoder.rex(.{
1015 .w = true,
1016 .b = ops.reg1.isExtended(),
1017 });
1018 encoder.opcode_withReg(opc, ops.reg1.lowId());
1019 break :blk true;
1020 }
1021 break :blk false;
1022 }
10231082 if (ops.reg1 == .none) {
10241083 // movabs moffs64, rax
10251084 const opc: u8 = if (ops.reg2.size() == 8) 0xa2 else 0xa3;
......@@ -1486,3 +1545,24 @@ test "lower MR encoding" {
14861545 "sub qword ptr [r11 + 0x10000000], r12",
14871546 );
14881547}
1548
1549test "lower OI encoding" {
1550 var code = TestEmitCode.init();
1551 defer code.deinit();
1552 try lowerToOiEnc(.mov, .rax, 0x1000000000000000, code.buffer());
1553 try expectEqualHexStrings(
1554 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
1555 code.emitted(),
1556 "movabs rax, 0x1000000000000000",
1557 );
1558 try lowerToOiEnc(.mov, .r11, 0x1000000000000000, code.buffer());
1559 try expectEqualHexStrings(
1560 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
1561 code.emitted(),
1562 "movabs r11, 0x1000000000000000",
1563 );
1564 try lowerToOiEnc(.mov, .r11d, 0x10000000, code.buffer());
1565 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", code.emitted(), "mov r11d, 0x10000000");
1566 try lowerToOiEnc(.mov, .r11b, 0x10, code.buffer());
1567 try expectEqualHexStrings("\x41\xB3\x10", code.emitted(), "mov r11b, 0x10");
1568}