| ... | @@ -193,8 +193,7 @@ fn mirNop(emit: *Emit) InnerError!void { | ... | @@ -193,8 +193,7 @@ fn mirNop(emit: *Emit) InnerError!void { |
| 193 | } | 193 | } |
| 194 | | 194 | |
| 195 | fn mirSyscall(emit: *Emit) InnerError!void { | 195 | fn mirSyscall(emit: *Emit) InnerError!void { |
| 196 | const encoder = try Encoder.init(emit.code, 2); | 196 | return lowerToZoEnc(.syscall, emit.code); |
| 197 | encoder.opcode_2byte(0x0f, 0x05); | | |
| 198 | } | 197 | } |
| 199 | | 198 | |
| 200 | fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 199 | fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| ... | @@ -470,6 +469,7 @@ const Tag = enum { | ... | @@ -470,6 +469,7 @@ const Tag = enum { |
| 470 | @"test", | 469 | @"test", |
| 471 | brk, | 470 | brk, |
| 472 | nop, | 471 | nop, |
| | 472 | syscall, |
| 473 | ret_near, | 473 | ret_near, |
| 474 | ret_far, | 474 | ret_far, |
| 475 | }; | 475 | }; |
| ... | @@ -509,78 +509,104 @@ const Encoding = enum { | ... | @@ -509,78 +509,104 @@ const Encoding = enum { |
| 509 | td, | 509 | td, |
| 510 | }; | 510 | }; |
| 511 | | 511 | |
| 512 | inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 { | 512 | const OpCode = union(enum) { |
| | 513 | one_byte: u8, |
| | 514 | two_byte: struct { _1: u8, _2: u8 }, |
| | 515 | |
| | 516 | fn oneByte(opc: u8) OpCode { |
| | 517 | return .{ .one_byte = opc }; |
| | 518 | } |
| | 519 | |
| | 520 | fn twoByte(opc1: u8, opc2: u8) OpCode { |
| | 521 | return .{ .two_byte = .{ ._1 = opc1, ._2 = opc2 } }; |
| | 522 | } |
| | 523 | |
| | 524 | fn encode(opc: OpCode, encoder: Encoder) void { |
| | 525 | switch (opc) { |
| | 526 | .one_byte => |v| encoder.opcode_1byte(v), |
| | 527 | .two_byte => |v| encoder.opcode_2byte(v._1, v._2), |
| | 528 | } |
| | 529 | } |
| | 530 | |
| | 531 | fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void { |
| | 532 | assert(opc == .one_byte); |
| | 533 | encoder.opcode_withReg(opc.one_byte, reg.lowId()); |
| | 534 | } |
| | 535 | }; |
| | 536 | |
| | 537 | inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { |
| 513 | switch (enc) { | 538 | switch (enc) { |
| 514 | .zo => return switch (tag) { | 539 | .zo => return switch (tag) { |
| 515 | .ret_near => 0xc3, | 540 | .ret_near => OpCode.oneByte(0xc3), |
| 516 | .ret_far => 0xcb, | 541 | .ret_far => OpCode.oneByte(0xcb), |
| 517 | .brk => 0xcc, | 542 | .brk => OpCode.oneByte(0xcc), |
| 518 | .nop => 0x90, | 543 | .nop => OpCode.oneByte(0x90), |
| | 544 | .syscall => OpCode.twoByte(0x0f, 0x05), |
| 519 | else => null, | 545 | else => null, |
| 520 | }, | 546 | }, |
| 521 | .d => return switch (tag) { | 547 | .d => return switch (tag) { |
| 522 | .jmp_near => 0xe9, | 548 | .jmp_near => OpCode.oneByte(0xe9), |
| 523 | .call_near => 0xe8, | 549 | .call_near => OpCode.oneByte(0xe8), |
| 524 | else => null, | 550 | else => null, |
| 525 | }, | 551 | }, |
| 526 | .m => return switch (tag) { | 552 | .m => return switch (tag) { |
| 527 | .jmp_near, .call_near, .push => 0xff, | 553 | .jmp_near, .call_near, .push => OpCode.oneByte(0xff), |
| 528 | .pop => 0x8f, | 554 | .pop => OpCode.oneByte(0x8f), |
| 529 | else => null, | 555 | else => null, |
| 530 | }, | 556 | }, |
| 531 | .o => return switch (tag) { | 557 | .o => return switch (tag) { |
| 532 | .push => 0x50, | 558 | .push => OpCode.oneByte(0x50), |
| 533 | .pop => 0x58, | 559 | .pop => OpCode.oneByte(0x58), |
| 534 | else => null, | 560 | else => null, |
| 535 | }, | 561 | }, |
| 536 | .i => return switch (tag) { | 562 | .i => return switch (tag) { |
| 537 | .push => 0x68, | 563 | .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68), |
| 538 | .@"test" => 0xa9, | 564 | .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9), |
| 539 | .ret_near => 0xc2, | 565 | .ret_near => OpCode.oneByte(0xc2), |
| 540 | .ret_far => 0xca, | 566 | .ret_far => OpCode.oneByte(0xca), |
| 541 | else => null, | 567 | else => null, |
| 542 | }, | 568 | }, |
| 543 | .mi => return switch (tag) { | 569 | .mi => return switch (tag) { |
| 544 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81, | 570 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), |
| 545 | .mov => 0xc7, | 571 | .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), |
| 546 | .@"test" => 0xf7, | 572 | .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), |
| 547 | else => null, | 573 | else => null, |
| 548 | }, | 574 | }, |
| 549 | .mr => return switch (tag) { | 575 | .mr => return switch (tag) { |
| 550 | .adc => 0x11, | 576 | .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), |
| 551 | .add => 0x01, | 577 | .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01), |
| 552 | .sub => 0x29, | 578 | .sub => OpCode.oneByte(if (is_one_byte) 0x28 else 0x29), |
| 553 | .xor => 0x31, | 579 | .xor => OpCode.oneByte(if (is_one_byte) 0x30 else 0x31), |
| 554 | .@"and" => 0x21, | 580 | .@"and" => OpCode.oneByte(if (is_one_byte) 0x20 else 0x21), |
| 555 | .@"or" => 0x09, | 581 | .@"or" => OpCode.oneByte(if (is_one_byte) 0x08 else 0x09), |
| 556 | .sbb => 0x19, | 582 | .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19), |
| 557 | .cmp => 0x39, | 583 | .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39), |
| 558 | .mov => 0x89, | 584 | .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89), |
| 559 | else => null, | 585 | else => null, |
| 560 | }, | 586 | }, |
| 561 | .rm => return switch (tag) { | 587 | .rm => return switch (tag) { |
| 562 | .adc => 0x13, | 588 | .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13), |
| 563 | .add => 0x03, | 589 | .add => OpCode.oneByte(if (is_one_byte) 0x02 else 0x03), |
| 564 | .sub => 0x2b, | 590 | .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b), |
| 565 | .xor => 0x33, | 591 | .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33), |
| 566 | .@"and" => 0x23, | 592 | .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23), |
| 567 | .@"or" => 0x0b, | 593 | .@"or" => OpCode.oneByte(if (is_one_byte) 0x0b else 0x0b), |
| 568 | .sbb => 0x1b, | 594 | .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b), |
| 569 | .cmp => 0x3b, | 595 | .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b), |
| 570 | .mov => 0x8b, | 596 | .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b), |
| 571 | .lea => 0x8d, | 597 | .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d), |
| 572 | else => null, | 598 | else => null, |
| 573 | }, | 599 | }, |
| 574 | .oi => return switch (tag) { | 600 | .oi => return switch (tag) { |
| 575 | .mov => 0xb8, | 601 | .mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8), |
| 576 | else => null, | 602 | else => null, |
| 577 | }, | 603 | }, |
| 578 | .fd => return switch (tag) { | 604 | .fd => return switch (tag) { |
| 579 | .mov => 0xa1, | 605 | .mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1), |
| 580 | else => null, | 606 | else => null, |
| 581 | }, | 607 | }, |
| 582 | .td => return switch (tag) { | 608 | .td => return switch (tag) { |
| 583 | .mov => 0xa3, | 609 | .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3), |
| 584 | else => null, | 610 | else => null, |
| 585 | }, | 611 | }, |
| 586 | } | 612 | } |
| ... | @@ -648,32 +674,25 @@ const RegisterOrMemory = union(enum) { | ... | @@ -648,32 +674,25 @@ const RegisterOrMemory = union(enum) { |
| 648 | }; | 674 | }; |
| 649 | | 675 | |
| 650 | fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { | 676 | fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { |
| 651 | const opc = getOpCode(tag, .zo).?; | 677 | const opc = getOpCode(tag, .zo, false).?; |
| 652 | const encoder = try Encoder.init(code, 1); | 678 | const encoder = try Encoder.init(code, 1); |
| 653 | encoder.opcode_1byte(opc); | 679 | opc.encode(encoder); |
| 654 | } | 680 | } |
| 655 | | 681 | |
| 656 | fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | 682 | fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 657 | var opc = getOpCode(tag, .i).?; | | |
| 658 | if (tag == .ret_far or tag == .ret_near) { | 683 | if (tag == .ret_far or tag == .ret_near) { |
| 659 | const encoder = try Encoder.init(code, 3); | 684 | const encoder = try Encoder.init(code, 3); |
| 660 | encoder.opcode_1byte(opc); | 685 | const opc = getOpCode(tag, .i, false).?; |
| | 686 | opc.encode(encoder); |
| 661 | encoder.imm16(@intCast(i16, imm)); | 687 | encoder.imm16(@intCast(i16, imm)); |
| 662 | return; | 688 | return; |
| 663 | } | 689 | } |
| 664 | if (immOpSize(imm) == 8) { | 690 | const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?; |
| 665 | // TODO I think getOpCode should track this | | |
| 666 | switch (tag) { | | |
| 667 | .push => opc += 2, | | |
| 668 | .@"test" => opc -= 1, | | |
| 669 | else => return error.EmitFail, | | |
| 670 | } | | |
| 671 | } | | |
| 672 | const encoder = try Encoder.init(code, 5); | 691 | const encoder = try Encoder.init(code, 5); |
| 673 | if (immOpSize(imm) == 16) { | 692 | if (immOpSize(imm) == 16) { |
| 674 | encoder.opcode_1byte(0x66); | 693 | encoder.opcode_1byte(0x66); |
| 675 | } | 694 | } |
| 676 | encoder.opcode_1byte(opc); | 695 | opc.encode(encoder); |
| 677 | if (immOpSize(imm) == 8) { | 696 | if (immOpSize(imm) == 8) { |
| 678 | encoder.imm8(@intCast(i8, imm)); | 697 | encoder.imm8(@intCast(i8, imm)); |
| 679 | } else if (immOpSize(imm) == 16) { | 698 | } else if (immOpSize(imm) == 16) { |
| ... | @@ -685,7 +704,7 @@ fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | ... | @@ -685,7 +704,7 @@ fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 685 | | 704 | |
| 686 | fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { | 705 | fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { |
| 687 | if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal? | 706 | if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal? |
| 688 | const opc = getOpCode(tag, .o).?; | 707 | const opc = getOpCode(tag, .o, false).?; |
| 689 | const encoder = try Encoder.init(code, 3); | 708 | const encoder = try Encoder.init(code, 3); |
| 690 | if (reg.size() == 16) { | 709 | if (reg.size() == 16) { |
| 691 | encoder.opcode_1byte(0x66); | 710 | encoder.opcode_1byte(0x66); |
| ... | @@ -694,18 +713,18 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi | ... | @@ -694,18 +713,18 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi |
| 694 | .w = false, | 713 | .w = false, |
| 695 | .b = reg.isExtended(), | 714 | .b = reg.isExtended(), |
| 696 | }); | 715 | }); |
| 697 | encoder.opcode_withReg(opc, reg.lowId()); | 716 | opc.encodeWithReg(encoder, reg); |
| 698 | } | 717 | } |
| 699 | | 718 | |
| 700 | fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | 719 | fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 701 | const opc = getOpCode(tag, .d).?; | 720 | const opc = getOpCode(tag, .d, false).?; |
| 702 | const encoder = try Encoder.init(code, 5); | 721 | const encoder = try Encoder.init(code, 5); |
| 703 | encoder.opcode_1byte(opc); | 722 | opc.encode(encoder); |
| 704 | encoder.imm32(imm); | 723 | encoder.imm32(imm); |
| 705 | } | 724 | } |
| 706 | | 725 | |
| 707 | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { | 726 | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| 708 | const opc = getOpCode(tag, .m).?; | 727 | const opc = getOpCode(tag, .m, false).?; |
| 709 | const modrm_ext = getModRmExt(tag).?; | 728 | const modrm_ext = getModRmExt(tag).?; |
| 710 | switch (reg_or_mem) { | 729 | switch (reg_or_mem) { |
| 711 | .register => |reg| { | 730 | .register => |reg| { |
| ... | @@ -715,7 +734,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) | ... | @@ -715,7 +734,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 715 | .w = false, | 734 | .w = false, |
| 716 | .b = reg.isExtended(), | 735 | .b = reg.isExtended(), |
| 717 | }); | 736 | }); |
| 718 | encoder.opcode_1byte(opc); | 737 | opc.encode(encoder); |
| 719 | encoder.modRm_direct(modrm_ext, reg.lowId()); | 738 | encoder.modRm_direct(modrm_ext, reg.lowId()); |
| 720 | }, | 739 | }, |
| 721 | .memory => |mem_op| { | 740 | .memory => |mem_op| { |
| ... | @@ -726,7 +745,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) | ... | @@ -726,7 +745,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 726 | .w = false, | 745 | .w = false, |
| 727 | .b = reg.isExtended(), | 746 | .b = reg.isExtended(), |
| 728 | }); | 747 | }); |
| 729 | encoder.opcode_1byte(opc); | 748 | opc.encode(encoder); |
| 730 | if (reg.lowId() == 4) { | 749 | if (reg.lowId() == 4) { |
| 731 | if (mem_op.disp == 0) { | 750 | if (mem_op.disp == 0) { |
| 732 | encoder.modRm_SIBDisp0(modrm_ext); | 751 | encoder.modRm_SIBDisp0(modrm_ext); |
| ... | @@ -752,7 +771,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) | ... | @@ -752,7 +771,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 752 | } | 771 | } |
| 753 | } | 772 | } |
| 754 | } else { | 773 | } else { |
| 755 | encoder.opcode_1byte(opc); | 774 | opc.encode(encoder); |
| 756 | if (mem_op.rip) { | 775 | if (mem_op.rip) { |
| 757 | encoder.modRm_RIPDisp32(modrm_ext); | 776 | encoder.modRm_RIPDisp32(modrm_ext); |
| 758 | } else { | 777 | } else { |
| ... | @@ -776,10 +795,10 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) I | ... | @@ -776,10 +795,10 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) I |
| 776 | fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) InnerError!void { | 795 | fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) InnerError!void { |
| 777 | if (reg.lowId() != Register.rax.lowId()) return error.EmitFail; | 796 | if (reg.lowId() != Register.rax.lowId()) return error.EmitFail; |
| 778 | if (reg.size() != immOpSize(moffs)) return error.EmitFail; | 797 | if (reg.size() != immOpSize(moffs)) return error.EmitFail; |
| 779 | var opc = if (td) getOpCode(tag, .td).? else getOpCode(tag, .fd).?; | 798 | const opc = if (td) |
| 780 | if (reg.size() == 8) { | 799 | getOpCode(tag, .td, reg.size() == 8).? |
| 781 | opc -= 1; | 800 | else |
| 782 | } | 801 | getOpCode(tag, .fd, reg.size() == 8).?; |
| 783 | const encoder = try Encoder.init(code, 10); | 802 | const encoder = try Encoder.init(code, 10); |
| 784 | if (reg.size() == 16) { | 803 | if (reg.size() == 16) { |
| 785 | encoder.opcode_1byte(0x66); | 804 | encoder.opcode_1byte(0x66); |
| ... | @@ -787,7 +806,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), | ... | @@ -787,7 +806,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), |
| 787 | encoder.rex(.{ | 806 | encoder.rex(.{ |
| 788 | .w = reg.size() == 64, | 807 | .w = reg.size() == 64, |
| 789 | }); | 808 | }); |
| 790 | encoder.opcode_1byte(opc); | 809 | opc.encode(encoder); |
| 791 | switch (reg.size()) { | 810 | switch (reg.size()) { |
| 792 | 8 => { | 811 | 8 => { |
| 793 | const moffs8 = try math.cast(i8, moffs); | 812 | const moffs8 = try math.cast(i8, moffs); |
| ... | @@ -809,11 +828,8 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), | ... | @@ -809,11 +828,8 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), |
| 809 | } | 828 | } |
| 810 | | 829 | |
| 811 | fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) InnerError!void { | 830 | fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) InnerError!void { |
| 812 | var opc = getOpCode(tag, .oi).?; | | |
| 813 | if (reg.size() != immOpSize(imm)) return error.EmitFail; | 831 | if (reg.size() != immOpSize(imm)) return error.EmitFail; |
| 814 | if (reg.size() == 8) { | 832 | const opc = getOpCode(tag, .oi, reg.size() == 8).?; |
| 815 | opc -= 8; | | |
| 816 | } | | |
| 817 | const encoder = try Encoder.init(code, 10); | 833 | const encoder = try Encoder.init(code, 10); |
| 818 | if (reg.size() == 16) { | 834 | if (reg.size() == 16) { |
| 819 | encoder.opcode_1byte(0x66); | 835 | encoder.opcode_1byte(0x66); |
| ... | @@ -822,7 +838,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn | ... | @@ -822,7 +838,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn |
| 822 | .w = reg.size() == 64, | 838 | .w = reg.size() == 64, |
| 823 | .b = reg.isExtended(), | 839 | .b = reg.isExtended(), |
| 824 | }); | 840 | }); |
| 825 | encoder.opcode_withReg(opc, reg.lowId()); | 841 | opc.encodeWithReg(encoder, reg); |
| 826 | switch (reg.size()) { | 842 | switch (reg.size()) { |
| 827 | 8 => { | 843 | 8 => { |
| 828 | const imm8 = try math.cast(i8, imm); | 844 | const imm8 = try math.cast(i8, imm); |
| ... | @@ -844,13 +860,10 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn | ... | @@ -844,13 +860,10 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn |
| 844 | } | 860 | } |
| 845 | | 861 | |
| 846 | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | 862 | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 847 | var opc = getOpCode(tag, .mi).?; | | |
| 848 | const modrm_ext = getModRmExt(tag).?; | 863 | const modrm_ext = getModRmExt(tag).?; |
| 849 | switch (reg_or_mem) { | 864 | switch (reg_or_mem) { |
| 850 | .register => |dst_reg| { | 865 | .register => |dst_reg| { |
| 851 | if (dst_reg.size() == 8) { | 866 | const opc = getOpCode(tag, .mi, dst_reg.size() == 8).?; |
| 852 | opc -= 1; | | |
| 853 | } | | |
| 854 | const encoder = try Encoder.init(code, 7); | 867 | const encoder = try Encoder.init(code, 7); |
| 855 | if (dst_reg.size() == 16) { | 868 | if (dst_reg.size() == 16) { |
| 856 | // 0x66 prefix switches to the non-default size; here we assume a switch from | 869 | // 0x66 prefix switches to the non-default size; here we assume a switch from |
| ... | @@ -862,7 +875,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr | ... | @@ -862,7 +875,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 862 | .w = dst_reg.size() == 64, | 875 | .w = dst_reg.size() == 64, |
| 863 | .b = dst_reg.isExtended(), | 876 | .b = dst_reg.isExtended(), |
| 864 | }); | 877 | }); |
| 865 | encoder.opcode_1byte(opc); | 878 | opc.encode(encoder); |
| 866 | encoder.modRm_direct(modrm_ext, dst_reg.lowId()); | 879 | encoder.modRm_direct(modrm_ext, dst_reg.lowId()); |
| 867 | switch (dst_reg.size()) { | 880 | switch (dst_reg.size()) { |
| 868 | 8 => { | 881 | 8 => { |
| ... | @@ -878,6 +891,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr | ... | @@ -878,6 +891,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 878 | } | 891 | } |
| 879 | }, | 892 | }, |
| 880 | .memory => |dst_mem| { | 893 | .memory => |dst_mem| { |
| | 894 | const opc = getOpCode(tag, .mi, false).?; |
| 881 | const encoder = try Encoder.init(code, 12); | 895 | const encoder = try Encoder.init(code, 12); |
| 882 | if (dst_mem.reg) |dst_reg| { | 896 | if (dst_mem.reg) |dst_reg| { |
| 883 | // Register dst_reg can either be 64bit or 32bit in size. | 897 | // Register dst_reg can either be 64bit or 32bit in size. |
| ... | @@ -891,7 +905,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr | ... | @@ -891,7 +905,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 891 | .w = false, | 905 | .w = false, |
| 892 | .b = dst_reg.isExtended(), | 906 | .b = dst_reg.isExtended(), |
| 893 | }); | 907 | }); |
| 894 | encoder.opcode_1byte(opc); | 908 | opc.encode(encoder); |
| 895 | if (dst_reg.lowId() == 4) { | 909 | if (dst_reg.lowId() == 4) { |
| 896 | if (dst_mem.disp == 0) { | 910 | if (dst_mem.disp == 0) { |
| 897 | encoder.modRm_SIBDisp0(modrm_ext); | 911 | encoder.modRm_SIBDisp0(modrm_ext); |
| ... | @@ -917,7 +931,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr | ... | @@ -917,7 +931,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 917 | } | 931 | } |
| 918 | } | 932 | } |
| 919 | } else { | 933 | } else { |
| 920 | encoder.opcode_1byte(opc); | 934 | opc.encode(encoder); |
| 921 | if (dst_mem.rip) { | 935 | if (dst_mem.rip) { |
| 922 | encoder.modRm_RIPDisp32(modrm_ext); | 936 | encoder.modRm_RIPDisp32(modrm_ext); |
| 923 | } else { | 937 | } else { |
| ... | @@ -937,10 +951,7 @@ fn lowerToRmEnc( | ... | @@ -937,10 +951,7 @@ fn lowerToRmEnc( |
| 937 | reg_or_mem: RegisterOrMemory, | 951 | reg_or_mem: RegisterOrMemory, |
| 938 | code: *std.ArrayList(u8), | 952 | code: *std.ArrayList(u8), |
| 939 | ) InnerError!void { | 953 | ) InnerError!void { |
| 940 | var opc = getOpCode(tag, .rm).?; | 954 | const opc = getOpCode(tag, .rm, reg.size() == 8).?; |
| 941 | if (reg.size() == 8) { | | |
| 942 | opc -= 1; | | |
| 943 | } | | |
| 944 | switch (reg_or_mem) { | 955 | switch (reg_or_mem) { |
| 945 | .register => |src_reg| { | 956 | .register => |src_reg| { |
| 946 | if (reg.size() != src_reg.size()) return error.EmitFail; | 957 | if (reg.size() != src_reg.size()) return error.EmitFail; |
| ... | @@ -950,7 +961,7 @@ fn lowerToRmEnc( | ... | @@ -950,7 +961,7 @@ fn lowerToRmEnc( |
| 950 | .r = reg.isExtended(), | 961 | .r = reg.isExtended(), |
| 951 | .b = src_reg.isExtended(), | 962 | .b = src_reg.isExtended(), |
| 952 | }); | 963 | }); |
| 953 | encoder.opcode_1byte(opc); | 964 | opc.encode(encoder); |
| 954 | encoder.modRm_direct(reg.lowId(), src_reg.lowId()); | 965 | encoder.modRm_direct(reg.lowId(), src_reg.lowId()); |
| 955 | }, | 966 | }, |
| 956 | .memory => |src_mem| { | 967 | .memory => |src_mem| { |
| ... | @@ -967,7 +978,7 @@ fn lowerToRmEnc( | ... | @@ -967,7 +978,7 @@ fn lowerToRmEnc( |
| 967 | .r = reg.isExtended(), | 978 | .r = reg.isExtended(), |
| 968 | .b = src_reg.isExtended(), | 979 | .b = src_reg.isExtended(), |
| 969 | }); | 980 | }); |
| 970 | encoder.opcode_1byte(opc); | 981 | opc.encode(encoder); |
| 971 | if (src_reg.lowId() == 4) { | 982 | if (src_reg.lowId() == 4) { |
| 972 | if (src_mem.disp == 0) { | 983 | if (src_mem.disp == 0) { |
| 973 | encoder.modRm_SIBDisp0(reg.lowId()); | 984 | encoder.modRm_SIBDisp0(reg.lowId()); |
| ... | @@ -997,7 +1008,7 @@ fn lowerToRmEnc( | ... | @@ -997,7 +1008,7 @@ fn lowerToRmEnc( |
| 997 | .w = reg.size() == 64, | 1008 | .w = reg.size() == 64, |
| 998 | .r = reg.isExtended(), | 1009 | .r = reg.isExtended(), |
| 999 | }); | 1010 | }); |
| 1000 | encoder.opcode_1byte(opc); | 1011 | opc.encode(encoder); |
| 1001 | if (src_mem.rip) { | 1012 | if (src_mem.rip) { |
| 1002 | encoder.modRm_RIPDisp32(reg.lowId()); | 1013 | encoder.modRm_RIPDisp32(reg.lowId()); |
| 1003 | } else { | 1014 | } else { |
| ... | @@ -1022,10 +1033,7 @@ fn lowerToMrEnc( | ... | @@ -1022,10 +1033,7 @@ fn lowerToMrEnc( |
| 1022 | // * reg is 32bit - dword ptr | 1033 | // * reg is 32bit - dword ptr |
| 1023 | // * reg is 16bit - word ptr | 1034 | // * reg is 16bit - word ptr |
| 1024 | // * reg is 8bit - byte ptr | 1035 | // * reg is 8bit - byte ptr |
| 1025 | var opc = getOpCode(tag, .mr).?; | 1036 | const opc = getOpCode(tag, .mr, reg.size() == 8).?; |
| 1026 | if (reg.size() == 8) { | | |
| 1027 | opc -= 1; | | |
| 1028 | } | | |
| 1029 | switch (reg_or_mem) { | 1037 | switch (reg_or_mem) { |
| 1030 | .register => |dst_reg| { | 1038 | .register => |dst_reg| { |
| 1031 | if (dst_reg.size() != reg.size()) return error.EmitFail; | 1039 | if (dst_reg.size() != reg.size()) return error.EmitFail; |
| ... | @@ -1035,7 +1043,7 @@ fn lowerToMrEnc( | ... | @@ -1035,7 +1043,7 @@ fn lowerToMrEnc( |
| 1035 | .r = reg.isExtended(), | 1043 | .r = reg.isExtended(), |
| 1036 | .b = dst_reg.isExtended(), | 1044 | .b = dst_reg.isExtended(), |
| 1037 | }); | 1045 | }); |
| 1038 | encoder.opcode_1byte(opc); | 1046 | opc.encode(encoder); |
| 1039 | encoder.modRm_direct(reg.lowId(), dst_reg.lowId()); | 1047 | encoder.modRm_direct(reg.lowId(), dst_reg.lowId()); |
| 1040 | }, | 1048 | }, |
| 1041 | .memory => |dst_mem| { | 1049 | .memory => |dst_mem| { |
| ... | @@ -1050,7 +1058,7 @@ fn lowerToMrEnc( | ... | @@ -1050,7 +1058,7 @@ fn lowerToMrEnc( |
| 1050 | .r = reg.isExtended(), | 1058 | .r = reg.isExtended(), |
| 1051 | .b = dst_reg.isExtended(), | 1059 | .b = dst_reg.isExtended(), |
| 1052 | }); | 1060 | }); |
| 1053 | encoder.opcode_1byte(opc); | 1061 | opc.encode(encoder); |
| 1054 | if (dst_reg.lowId() == 4) { | 1062 | if (dst_reg.lowId() == 4) { |
| 1055 | if (dst_mem.disp == 0) { | 1063 | if (dst_mem.disp == 0) { |
| 1056 | encoder.modRm_SIBDisp0(reg.lowId()); | 1064 | encoder.modRm_SIBDisp0(reg.lowId()); |
| ... | @@ -1080,7 +1088,7 @@ fn lowerToMrEnc( | ... | @@ -1080,7 +1088,7 @@ fn lowerToMrEnc( |
| 1080 | .w = reg.size() == 64, | 1088 | .w = reg.size() == 64, |
| 1081 | .r = reg.isExtended(), | 1089 | .r = reg.isExtended(), |
| 1082 | }); | 1090 | }); |
| 1083 | encoder.opcode_1byte(opc); | 1091 | opc.encode(encoder); |
| 1084 | if (dst_mem.rip) { | 1092 | if (dst_mem.rip) { |
| 1085 | encoder.modRm_RIPDisp32(reg.lowId()); | 1093 | encoder.modRm_RIPDisp32(reg.lowId()); |
| 1086 | } else { | 1094 | } else { |
| ... | @@ -1168,10 +1176,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1168,10 +1176,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1168 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); | 1176 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1169 | const scale = ops.flags; | 1177 | const scale = ops.flags; |
| 1170 | // OP reg1, [reg2 + scale*rcx + imm32] | 1178 | // OP reg1, [reg2 + scale*rcx + imm32] |
| 1171 | var opc = getOpCode(tag, .rm).?; | 1179 | const opc = getOpCode(tag, .rm, ops.reg1.size() == 8).?; |
| 1172 | if (ops.reg1.size() == 8) { | | |
| 1173 | opc -= 1; | | |
| 1174 | } | | |
| 1175 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 1180 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 1176 | const encoder = try Encoder.init(emit.code, 8); | 1181 | const encoder = try Encoder.init(emit.code, 8); |
| 1177 | encoder.rex(.{ | 1182 | encoder.rex(.{ |
| ... | @@ -1179,7 +1184,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1179,7 +1184,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1179 | .r = ops.reg1.isExtended(), | 1184 | .r = ops.reg1.isExtended(), |
| 1180 | .b = ops.reg2.isExtended(), | 1185 | .b = ops.reg2.isExtended(), |
| 1181 | }); | 1186 | }); |
| 1182 | encoder.opcode_1byte(opc); | 1187 | opc.encode(encoder); |
| 1183 | if (imm <= math.maxInt(i8)) { | 1188 | if (imm <= math.maxInt(i8)) { |
| 1184 | encoder.modRm_SIBDisp8(ops.reg1.lowId()); | 1189 | encoder.modRm_SIBDisp8(ops.reg1.lowId()); |
| 1185 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rcx.lowId(), ops.reg2.lowId()); | 1190 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rcx.lowId(), ops.reg2.lowId()); |
| ... | @@ -1198,17 +1203,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1198,17 +1203,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1198 | | 1203 | |
| 1199 | if (ops.reg2 == .none) { | 1204 | if (ops.reg2 == .none) { |
| 1200 | // OP [reg1 + scale*rax + 0], imm32 | 1205 | // OP [reg1 + scale*rax + 0], imm32 |
| 1201 | var opc = getOpCode(tag, .mi).?; | 1206 | const opc = getOpCode(tag, .mi, ops.reg1.size() == 8).?; |
| 1202 | const modrm_ext = getModRmExt(tag).?; | 1207 | const modrm_ext = getModRmExt(tag).?; |
| 1203 | if (ops.reg1.size() == 8) { | | |
| 1204 | opc -= 1; | | |
| 1205 | } | | |
| 1206 | const encoder = try Encoder.init(emit.code, 8); | 1208 | const encoder = try Encoder.init(emit.code, 8); |
| 1207 | encoder.rex(.{ | 1209 | encoder.rex(.{ |
| 1208 | .w = ops.reg1.size() == 64, | 1210 | .w = ops.reg1.size() == 64, |
| 1209 | .b = ops.reg1.isExtended(), | 1211 | .b = ops.reg1.isExtended(), |
| 1210 | }); | 1212 | }); |
| 1211 | encoder.opcode_1byte(opc); | 1213 | opc.encode(encoder); |
| 1212 | encoder.modRm_SIBDisp0(modrm_ext); | 1214 | encoder.modRm_SIBDisp0(modrm_ext); |
| 1213 | encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId()); | 1215 | encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId()); |
| 1214 | if (imm <= math.maxInt(i8)) { | 1216 | if (imm <= math.maxInt(i8)) { |
| ... | @@ -1222,17 +1224,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1222,17 +1224,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1222 | } | 1224 | } |
| 1223 | | 1225 | |
| 1224 | // OP [reg1 + scale*rax + imm32], reg2 | 1226 | // OP [reg1 + scale*rax + imm32], reg2 |
| 1225 | var opc = getOpCode(tag, .mr).?; | 1227 | const opc = getOpCode(tag, .mr, ops.reg1.size() == 8).?; |
| 1226 | if (ops.reg1.size() == 8) { | | |
| 1227 | opc -= 1; | | |
| 1228 | } | | |
| 1229 | const encoder = try Encoder.init(emit.code, 8); | 1228 | const encoder = try Encoder.init(emit.code, 8); |
| 1230 | encoder.rex(.{ | 1229 | encoder.rex(.{ |
| 1231 | .w = ops.reg1.size() == 64, | 1230 | .w = ops.reg1.size() == 64, |
| 1232 | .r = ops.reg2.isExtended(), | 1231 | .r = ops.reg2.isExtended(), |
| 1233 | .b = ops.reg1.isExtended(), | 1232 | .b = ops.reg1.isExtended(), |
| 1234 | }); | 1233 | }); |
| 1235 | encoder.opcode_1byte(opc); | 1234 | opc.encode(encoder); |
| 1236 | if (imm <= math.maxInt(i8)) { | 1235 | if (imm <= math.maxInt(i8)) { |
| 1237 | encoder.modRm_SIBDisp8(ops.reg2.lowId()); | 1236 | encoder.modRm_SIBDisp8(ops.reg2.lowId()); |
| 1238 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId()); | 1237 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId()); |
| ... | @@ -1249,17 +1248,14 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1249,17 +1248,14 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1249 | const scale = ops.flags; | 1248 | const scale = ops.flags; |
| 1250 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 1249 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 1251 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | 1250 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; |
| 1252 | var opc = getOpCode(tag, .mi).?; | 1251 | const opc = getOpCode(tag, .mi, ops.reg1.size() == 8).?; |
| 1253 | if (ops.reg1.size() == 8) { | | |
| 1254 | opc -= 1; | | |
| 1255 | } | | |
| 1256 | const modrm_ext = getModRmExt(tag).?; | 1252 | const modrm_ext = getModRmExt(tag).?; |
| 1257 | const encoder = try Encoder.init(emit.code, 2); | 1253 | const encoder = try Encoder.init(emit.code, 2); |
| 1258 | encoder.rex(.{ | 1254 | encoder.rex(.{ |
| 1259 | .w = ops.reg1.size() == 64, | 1255 | .w = ops.reg1.size() == 64, |
| 1260 | .b = ops.reg1.isExtended(), | 1256 | .b = ops.reg1.isExtended(), |
| 1261 | }); | 1257 | }); |
| 1262 | encoder.opcode_1byte(opc); | 1258 | opc.encode(encoder); |
| 1263 | if (imm_pair.dest_off <= math.maxInt(i8)) { | 1259 | if (imm_pair.dest_off <= math.maxInt(i8)) { |
| 1264 | encoder.modRm_SIBDisp8(modrm_ext); | 1260 | encoder.modRm_SIBDisp8(modrm_ext); |
| 1265 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId()); | 1261 | encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId()); |