| author | |
| committer | |
| log | e0167ae0e3f0e6c6a667b372b21c322d47ccd92d |
| tree | 27b8d51339716708ae8398f7683aa7f57e48e245 |
| parent | 619d82234ecbb8a130e827e9de6f545288649a58 |
This relieves register pressure, and reduce generated code size
(since now we can use the same index register for both `mov_scale_src`
and `mov_scale_dst` MIR instructions).
Fix lowering of ModRM + SIB encodings where index register is extended
- previously, we would carelessly ignore the fact generating incorrect
encodings.3 files changed, 154 insertions(+), 106 deletions(-)
src/arch/x86_64/CodeGen.zig+38-66| ... | ... | @@ -5932,7 +5932,6 @@ const InlineMemcpyOpts = struct { |
| 5932 | 5932 | dest_stack_base: ?Register = null, |
| 5933 | 5933 | }; |
| 5934 | 5934 | |
| 5935 | /// Spills .rax and .rcx. | |
| 5936 | 5935 | fn genInlineMemcpy( |
| 5937 | 5936 | self: *Self, |
| 5938 | 5937 | dst_ptr: MCValue, |
| ... | ... | @@ -5940,19 +5939,6 @@ fn genInlineMemcpy( |
| 5940 | 5939 | len: MCValue, |
| 5941 | 5940 | opts: InlineMemcpyOpts, |
| 5942 | 5941 | ) InnerError!void { |
| 5943 | // TODO: Preserve contents of .rax and .rcx if not free and locked, and then restore | |
| 5944 | // How can we do this without context if the value inside .rax or .rcx we preserve contains | |
| 5945 | // value needed to perform the memcpy in the first place? | |
| 5946 | // I think we should have an accumulator-based context that we pass with each subsequent helper | |
| 5947 | // call until we resolve the entire instruction. | |
| 5948 | try self.register_manager.getReg(.rax, null); | |
| 5949 | try self.register_manager.getReg(.rcx, null); | |
| 5950 | ||
| 5951 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }); | |
| 5952 | defer for (reg_locks) |lock| { | |
| 5953 | self.register_manager.unlockReg(lock); | |
| 5954 | }; | |
| 5955 | ||
| 5956 | 5942 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| |
| 5957 | 5943 | self.register_manager.lockReg(reg) |
| 5958 | 5944 | else |
| ... | ... | @@ -5965,7 +5951,13 @@ fn genInlineMemcpy( |
| 5965 | 5951 | null; |
| 5966 | 5952 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); |
| 5967 | 5953 | |
| 5968 | const dst_addr_reg = try self.register_manager.allocReg(null, gp); | |
| 5954 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); | |
| 5955 | const dst_addr_reg = regs[0]; | |
| 5956 | const src_addr_reg = regs[1]; | |
| 5957 | const index_reg = regs[2].to64(); | |
| 5958 | const count_reg = regs[3].to64(); | |
| 5959 | const tmp_reg = regs[4].to8(); | |
| 5960 | ||
| 5969 | 5961 | switch (dst_ptr) { |
| 5970 | 5962 | .memory, |
| 5971 | 5963 | .got_load, |
| ... | ... | @@ -5998,10 +5990,7 @@ fn genInlineMemcpy( |
| 5998 | 5990 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 5999 | 5991 | }, |
| 6000 | 5992 | } |
| 6001 | const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); | |
| 6002 | defer self.register_manager.unlockReg(dst_addr_reg_lock); | |
| 6003 | 5993 | |
| 6004 | const src_addr_reg = try self.register_manager.allocReg(null, gp); | |
| 6005 | 5994 | switch (src_ptr) { |
| 6006 | 5995 | .memory, |
| 6007 | 5996 | .got_load, |
| ... | ... | @@ -6034,26 +6023,13 @@ fn genInlineMemcpy( |
| 6034 | 6023 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); |
| 6035 | 6024 | }, |
| 6036 | 6025 | } |
| 6037 | const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); | |
| 6038 | defer self.register_manager.unlockReg(src_addr_reg_lock); | |
| 6039 | ||
| 6040 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | |
| 6041 | const count_reg = regs[0].to64(); | |
| 6042 | const tmp_reg = regs[1].to8(); | |
| 6043 | 6026 | |
| 6044 | 6027 | try self.genSetReg(Type.usize, count_reg, len); |
| 6045 | 6028 | |
| 6046 | // mov rcx, 0 | |
| 6047 | _ = try self.addInst(.{ | |
| 6048 | .tag = .mov, | |
| 6049 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | |
| 6050 | .data = .{ .imm = 0 }, | |
| 6051 | }); | |
| 6052 | ||
| 6053 | // mov rax, 0 | |
| 6029 | // mov index_reg, 0 | |
| 6054 | 6030 | _ = try self.addInst(.{ |
| 6055 | 6031 | .tag = .mov, |
| 6056 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6032 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6057 | 6033 | .data = .{ .imm = 0 }, |
| 6058 | 6034 | }); |
| 6059 | 6035 | |
| ... | ... | @@ -6075,37 +6051,30 @@ fn genInlineMemcpy( |
| 6075 | 6051 | } }, |
| 6076 | 6052 | }); |
| 6077 | 6053 | |
| 6078 | // mov tmp, [addr + rcx] | |
| 6054 | // mov tmp, [addr + index_reg] | |
| 6079 | 6055 | _ = try self.addInst(.{ |
| 6080 | 6056 | .tag = .mov_scale_src, |
| 6081 | 6057 | .ops = Mir.Inst.Ops.encode(.{ |
| 6082 | 6058 | .reg1 = tmp_reg.to8(), |
| 6083 | 6059 | .reg2 = src_addr_reg, |
| 6084 | 6060 | }), |
| 6085 | .data = .{ .imm = 0 }, | |
| 6061 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | |
| 6086 | 6062 | }); |
| 6087 | 6063 | |
| 6088 | // mov [stack_offset + rax], tmp | |
| 6064 | // mov [stack_offset + index_reg], tmp | |
| 6089 | 6065 | _ = try self.addInst(.{ |
| 6090 | 6066 | .tag = .mov_scale_dst, |
| 6091 | 6067 | .ops = Mir.Inst.Ops.encode(.{ |
| 6092 | 6068 | .reg1 = dst_addr_reg, |
| 6093 | 6069 | .reg2 = tmp_reg.to8(), |
| 6094 | 6070 | }), |
| 6095 | .data = .{ .imm = 0 }, | |
| 6071 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | |
| 6096 | 6072 | }); |
| 6097 | 6073 | |
| 6098 | // add rcx, 1 | |
| 6074 | // add index_reg, 1 | |
| 6099 | 6075 | _ = try self.addInst(.{ |
| 6100 | 6076 | .tag = .add, |
| 6101 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | |
| 6102 | .data = .{ .imm = 1 }, | |
| 6103 | }); | |
| 6104 | ||
| 6105 | // add rax, 1 | |
| 6106 | _ = try self.addInst(.{ | |
| 6107 | .tag = .add, | |
| 6108 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6077 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6109 | 6078 | .data = .{ .imm = 1 }, |
| 6110 | 6079 | }); |
| 6111 | 6080 | |
| ... | ... | @@ -6127,7 +6096,6 @@ fn genInlineMemcpy( |
| 6127 | 6096 | try self.performReloc(loop_reloc); |
| 6128 | 6097 | } |
| 6129 | 6098 | |
| 6130 | /// Spills .rax register. | |
| 6131 | 6099 | fn genInlineMemset( |
| 6132 | 6100 | self: *Self, |
| 6133 | 6101 | dst_ptr: MCValue, |
| ... | ... | @@ -6135,12 +6103,22 @@ fn genInlineMemset( |
| 6135 | 6103 | len: MCValue, |
| 6136 | 6104 | opts: InlineMemcpyOpts, |
| 6137 | 6105 | ) InnerError!void { |
| 6138 | // TODO preserve contents of .rax and then restore | |
| 6139 | try self.register_manager.getReg(.rax, null); | |
| 6140 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | |
| 6141 | defer self.register_manager.unlockReg(rax_lock); | |
| 6106 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| | |
| 6107 | self.register_manager.lockReg(reg) | |
| 6108 | else | |
| 6109 | null; | |
| 6110 | defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg); | |
| 6111 | ||
| 6112 | const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| | |
| 6113 | self.register_manager.lockReg(reg) | |
| 6114 | else | |
| 6115 | null; | |
| 6116 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | |
| 6117 | ||
| 6118 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | |
| 6119 | const addr_reg = regs[0]; | |
| 6120 | const index_reg = regs[1].to64(); | |
| 6142 | 6121 | |
| 6143 | const addr_reg = try self.register_manager.allocReg(null, gp); | |
| 6144 | 6122 | switch (dst_ptr) { |
| 6145 | 6123 | .memory, |
| 6146 | 6124 | .got_load, |
| ... | ... | @@ -6173,17 +6151,15 @@ fn genInlineMemset( |
| 6173 | 6151 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 6174 | 6152 | }, |
| 6175 | 6153 | } |
| 6176 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); | |
| 6177 | defer self.register_manager.unlockReg(addr_reg_lock); | |
| 6178 | 6154 | |
| 6179 | try self.genSetReg(Type.usize, .rax, len); | |
| 6180 | try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); | |
| 6155 | try self.genSetReg(Type.usize, index_reg, len); | |
| 6156 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); | |
| 6181 | 6157 | |
| 6182 | 6158 | // loop: |
| 6183 | // cmp rax, -1 | |
| 6159 | // cmp index_reg, -1 | |
| 6184 | 6160 | const loop_start = try self.addInst(.{ |
| 6185 | 6161 | .tag = .cmp, |
| 6186 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6162 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6187 | 6163 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, |
| 6188 | 6164 | }); |
| 6189 | 6165 | |
| ... | ... | @@ -6202,24 +6178,20 @@ fn genInlineMemset( |
| 6202 | 6178 | if (x > math.maxInt(i32)) { |
| 6203 | 6179 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); |
| 6204 | 6180 | } |
| 6205 | // mov byte ptr [rbp + rax + stack_offset], imm | |
| 6206 | const payload = try self.addExtra(Mir.ImmPair{ | |
| 6207 | .dest_off = 0, | |
| 6208 | .operand = @truncate(u32, x), | |
| 6209 | }); | |
| 6181 | // mov byte ptr [rbp + index_reg + stack_offset], imm | |
| 6210 | 6182 | _ = try self.addInst(.{ |
| 6211 | 6183 | .tag = .mov_mem_index_imm, |
| 6212 | 6184 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), |
| 6213 | .data = .{ .payload = payload }, | |
| 6185 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) }, | |
| 6214 | 6186 | }); |
| 6215 | 6187 | }, |
| 6216 | 6188 | else => return self.fail("TODO inline memset for value of type {}", .{value}), |
| 6217 | 6189 | } |
| 6218 | 6190 | |
| 6219 | // sub rax, 1 | |
| 6191 | // sub index_reg, 1 | |
| 6220 | 6192 | _ = try self.addInst(.{ |
| 6221 | 6193 | .tag = .sub, |
| 6222 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6194 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6223 | 6195 | .data = .{ .imm = 1 }, |
| 6224 | 6196 | }); |
| 6225 | 6197 |
src/arch/x86_64/Emit.zig+39-30| ... | ... | @@ -615,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 { |
| 615 | 615 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 616 | 616 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 617 | 617 | const scale = ops.flags; |
| 618 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 619 | // OP reg1, [reg2 + scale*rcx + imm32] | |
| 618 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 619 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 620 | // OP reg1, [reg2 + scale*index + imm32] | |
| 620 | 621 | const scale_index = ScaleIndex{ |
| 621 | 622 | .scale = scale, |
| 622 | .index = .rcx, | |
| 623 | .index = index_reg_disp.index, | |
| 623 | 624 | }; |
| 624 | 625 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 625 | .disp = imm, | |
| 626 | .disp = index_reg_disp.disp, | |
| 626 | 627 | .base = ops.reg2, |
| 627 | 628 | .scale_index = scale_index, |
| 628 | 629 | }), emit.code); |
| ... | ... | @@ -631,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 631 | 632 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 632 | 633 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 633 | 634 | const scale = ops.flags; |
| 634 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 635 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 636 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 635 | 637 | const scale_index = ScaleIndex{ |
| 636 | 638 | .scale = scale, |
| 637 | .index = .rax, | |
| 639 | .index = index_reg_disp.index, | |
| 638 | 640 | }; |
| 639 | if (ops.reg2 == .none) { | |
| 640 | // OP qword ptr [reg1 + scale*rax + 0], imm32 | |
| 641 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ | |
| 642 | .disp = 0, | |
| 643 | .base = ops.reg1, | |
| 644 | .scale_index = scale_index, | |
| 645 | }), imm, emit.code); | |
| 646 | } | |
| 647 | // OP [reg1 + scale*rax + imm32], reg2 | |
| 641 | assert(ops.reg2 != .none); | |
| 642 | // OP [reg1 + scale*index + imm32], reg2 | |
| 648 | 643 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ |
| 649 | .disp = imm, | |
| 644 | .disp = index_reg_disp.disp, | |
| 650 | 645 | .base = ops.reg1, |
| 651 | 646 | .scale_index = scale_index, |
| 652 | 647 | }), ops.reg2, emit.code); |
| ... | ... | @@ -656,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 656 | 651 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 657 | 652 | const scale = ops.flags; |
| 658 | 653 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 659 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | |
| 654 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | |
| 660 | 655 | const scale_index = ScaleIndex{ |
| 661 | 656 | .scale = scale, |
| 662 | .index = .rax, | |
| 657 | .index = index_reg_disp_imm.index, | |
| 663 | 658 | }; |
| 664 | // OP qword ptr [reg1 + scale*rax + imm32], imm32 | |
| 659 | // OP qword ptr [reg1 + scale*index + imm32], imm32 | |
| 665 | 660 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ |
| 666 | .disp = imm_pair.dest_off, | |
| 661 | .disp = index_reg_disp_imm.disp, | |
| 667 | 662 | .base = ops.reg1, |
| 668 | 663 | .scale_index = scale_index, |
| 669 | }), imm_pair.operand, emit.code); | |
| 664 | }), index_reg_disp_imm.imm, emit.code); | |
| 670 | 665 | } |
| 671 | 666 | |
| 672 | 667 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 673 | 668 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 674 | 669 | assert(ops.reg2 == .none); |
| 675 | 670 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 676 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | |
| 671 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | |
| 677 | 672 | const ptr_size: Memory.PtrSize = switch (ops.flags) { |
| 678 | 673 | 0b00 => .byte_ptr, |
| 679 | 674 | 0b01 => .word_ptr, |
| ... | ... | @@ -682,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v |
| 682 | 677 | }; |
| 683 | 678 | const scale_index = ScaleIndex{ |
| 684 | 679 | .scale = 0, |
| 685 | .index = .rax, | |
| 680 | .index = index_reg_disp_imm.index, | |
| 686 | 681 | }; |
| 687 | // OP ptr [reg1 + rax*1 + imm32], imm32 | |
| 682 | // OP ptr [reg1 + index + imm32], imm32 | |
| 688 | 683 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ |
| 689 | .disp = imm_pair.dest_off, | |
| 684 | .disp = index_reg_disp_imm.disp, | |
| 690 | 685 | .base = ops.reg1, |
| 691 | 686 | .scale_index = scale_index, |
| 692 | }), imm_pair.operand, emit.code); | |
| 687 | }), index_reg_disp_imm.imm, emit.code); | |
| 693 | 688 | } |
| 694 | 689 | |
| 695 | 690 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| ... | ... | @@ -957,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 957 | 952 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); |
| 958 | 953 | }, |
| 959 | 954 | 0b10 => { |
| 960 | // lea reg, [rbp + rcx + imm32] | |
| 961 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 955 | // lea reg, [rbp + index + imm32] | |
| 956 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 957 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 962 | 958 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; |
| 963 | 959 | const scale_index = ScaleIndex{ |
| 964 | 960 | .scale = 0, |
| 965 | .index = .rcx, | |
| 961 | .index = index_reg_disp.index, | |
| 966 | 962 | }; |
| 967 | 963 | return lowerToRmEnc( |
| 968 | 964 | .lea, |
| 969 | 965 | ops.reg1, |
| 970 | 966 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 971 | .disp = imm, | |
| 967 | .disp = index_reg_disp.disp, | |
| 972 | 968 | .base = src_reg, |
| 973 | 969 | .scale_index = scale_index, |
| 974 | 970 | }), |
| ... | ... | @@ -2255,6 +2251,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st |
| 2255 | 2251 | encoder.rex(.{ |
| 2256 | 2252 | .w = wide, |
| 2257 | 2253 | .b = base.isExtended(), |
| 2254 | .x = if (mem_op.scale_index) |si| si.index.isExtended() else false, | |
| 2258 | 2255 | }); |
| 2259 | 2256 | } |
| 2260 | 2257 | opc.encode(encoder); |
| ... | ... | @@ -2360,10 +2357,12 @@ fn lowerToMiXEnc( |
| 2360 | 2357 | encoder.rex(.{ |
| 2361 | 2358 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2362 | 2359 | .b = base.isExtended(), |
| 2360 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2363 | 2361 | }); |
| 2364 | 2362 | } else { |
| 2365 | 2363 | encoder.rex(.{ |
| 2366 | 2364 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2365 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2367 | 2366 | }); |
| 2368 | 2367 | } |
| 2369 | 2368 | opc.encode(encoder); |
| ... | ... | @@ -2415,11 +2414,13 @@ fn lowerToRmEnc( |
| 2415 | 2414 | .w = setRexWRegister(reg), |
| 2416 | 2415 | .r = reg.isExtended(), |
| 2417 | 2416 | .b = base.isExtended(), |
| 2417 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2418 | 2418 | }); |
| 2419 | 2419 | } else { |
| 2420 | 2420 | encoder.rex(.{ |
| 2421 | 2421 | .w = setRexWRegister(reg), |
| 2422 | 2422 | .r = reg.isExtended(), |
| 2423 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2423 | 2424 | }); |
| 2424 | 2425 | } |
| 2425 | 2426 | opc.encode(encoder); |
| ... | ... | @@ -2460,11 +2461,13 @@ fn lowerToMrEnc( |
| 2460 | 2461 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2461 | 2462 | .r = reg.isExtended(), |
| 2462 | 2463 | .b = base.isExtended(), |
| 2464 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2463 | 2465 | }); |
| 2464 | 2466 | } else { |
| 2465 | 2467 | encoder.rex(.{ |
| 2466 | 2468 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2467 | 2469 | .r = reg.isExtended(), |
| 2470 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2468 | 2471 | }); |
| 2469 | 2472 | } |
| 2470 | 2473 | opc.encode(encoder); |
| ... | ... | @@ -2504,11 +2507,13 @@ fn lowerToRmiEnc( |
| 2504 | 2507 | .w = setRexWRegister(reg), |
| 2505 | 2508 | .r = reg.isExtended(), |
| 2506 | 2509 | .b = base.isExtended(), |
| 2510 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2507 | 2511 | }); |
| 2508 | 2512 | } else { |
| 2509 | 2513 | encoder.rex(.{ |
| 2510 | 2514 | .w = setRexWRegister(reg), |
| 2511 | 2515 | .r = reg.isExtended(), |
| 2516 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2512 | 2517 | }); |
| 2513 | 2518 | } |
| 2514 | 2519 | opc.encode(encoder); |
| ... | ... | @@ -2545,10 +2550,12 @@ fn lowerToVmEnc( |
| 2545 | 2550 | vex.rex(.{ |
| 2546 | 2551 | .r = reg.isExtended(), |
| 2547 | 2552 | .b = base.isExtended(), |
| 2553 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2548 | 2554 | }); |
| 2549 | 2555 | } else { |
| 2550 | 2556 | vex.rex(.{ |
| 2551 | 2557 | .r = reg.isExtended(), |
| 2558 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2552 | 2559 | }); |
| 2553 | 2560 | } |
| 2554 | 2561 | encoder.vex(enc.prefix); |
| ... | ... | @@ -2585,10 +2592,12 @@ fn lowerToMvEnc( |
| 2585 | 2592 | vex.rex(.{ |
| 2586 | 2593 | .r = reg.isExtended(), |
| 2587 | 2594 | .b = base.isExtended(), |
| 2595 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2588 | 2596 | }); |
| 2589 | 2597 | } else { |
| 2590 | 2598 | vex.rex(.{ |
| 2591 | 2599 | .r = reg.isExtended(), |
| 2600 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2592 | 2601 | }); |
| 2593 | 2602 | } |
| 2594 | 2603 | encoder.vex(enc.prefix); |
src/arch/x86_64/Mir.zig+77-10| ... | ... | @@ -44,25 +44,28 @@ pub const Inst = struct { |
| 44 | 44 | /// 0b01 word ptr [reg1 + imm32], imm16 |
| 45 | 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 |
| 46 | 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) |
| 47 | /// Notes: | |
| 48 | /// * Uses `ImmPair` as payload | |
| 47 | 49 | adc_mem_imm, |
| 48 | 50 | |
| 49 | /// form: reg1, [reg2 + scale*rcx + imm32] | |
| 51 | /// form: reg1, [reg2 + scale*index + imm32] | |
| 50 | 52 | /// ops flags scale |
| 51 | 53 | /// 0b00 1 |
| 52 | 54 | /// 0b01 2 |
| 53 | 55 | /// 0b10 4 |
| 54 | 56 | /// 0b11 8 |
| 57 | /// Notes: | |
| 58 | /// * Uses `IndexRegisterDisp` as payload | |
| 55 | 59 | adc_scale_src, |
| 56 | 60 | |
| 57 | /// form: [reg1 + scale*rax + imm32], reg2 | |
| 58 | /// form: [reg1 + scale*rax + 0], imm32 | |
| 61 | /// form: [reg1 + scale*index + imm32], reg2 | |
| 59 | 62 | /// ops flags scale |
| 60 | 63 | /// 0b00 1 |
| 61 | 64 | /// 0b01 2 |
| 62 | 65 | /// 0b10 4 |
| 63 | 66 | /// 0b11 8 |
| 64 | 67 | /// Notes: |
| 65 | /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. | |
| 68 | /// * Uses `IndexRegisterDisp` payload. | |
| 66 | 69 | adc_scale_dst, |
| 67 | 70 | |
| 68 | 71 | /// form: [reg1 + scale*rax + imm32], imm32 |
| ... | ... | @@ -72,14 +75,16 @@ pub const Inst = struct { |
| 72 | 75 | /// 0b10 4 |
| 73 | 76 | /// 0b11 8 |
| 74 | 77 | /// Notes: |
| 75 | /// * Data field `payload` points at `ImmPair`. | |
| 78 | /// * Uses `IndexRegisterDispImm` payload. | |
| 76 | 79 | adc_scale_imm, |
| 77 | 80 | |
| 78 | 81 | /// ops flags: form: |
| 79 | /// 0b00 byte ptr [reg1 + rax + imm32], imm8 | |
| 80 | /// 0b01 word ptr [reg1 + rax + imm32], imm16 | |
| 81 | /// 0b10 dword ptr [reg1 + rax + imm32], imm32 | |
| 82 | /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64) | |
| 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 | |
| 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 | |
| 84 | /// 0b10 dword ptr [reg1 + index + imm32], imm32 | |
| 85 | /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) | |
| 86 | /// Notes: | |
| 87 | /// * Uses `IndexRegisterDispImm` payload. | |
| 83 | 88 | adc_mem_index_imm, |
| 84 | 89 | |
| 85 | 90 | // The following instructions all have the same encoding as `adc`. |
| ... | ... | @@ -174,7 +179,9 @@ pub const Inst = struct { |
| 174 | 179 | /// 0b00 reg1, [reg2 + imm32] |
| 175 | 180 | /// 0b00 reg1, [ds:imm32] |
| 176 | 181 | /// 0b01 reg1, [rip + imm32] |
| 177 | /// 0b10 reg1, [reg2 + rcx + imm32] | |
| 182 | /// 0b10 reg1, [reg2 + index + imm32] | |
| 183 | /// Notes: | |
| 184 | /// * 0b10 uses `IndexRegisterDisp` payload | |
| 178 | 185 | lea, |
| 179 | 186 | |
| 180 | 187 | /// ops flags: form: |
| ... | ... | @@ -461,6 +468,66 @@ pub const Inst = struct { |
| 461 | 468 | } |
| 462 | 469 | }; |
| 463 | 470 | |
| 471 | pub const IndexRegisterDisp = struct { | |
| 472 | /// Index register to use with SIB-based encoding | |
| 473 | index: u32, | |
| 474 | ||
| 475 | /// Displacement value | |
| 476 | disp: u32, | |
| 477 | ||
| 478 | pub fn encode(index: Register, disp: u32) IndexRegisterDisp { | |
| 479 | return .{ | |
| 480 | .index = @enumToInt(index), | |
| 481 | .disp = disp, | |
| 482 | }; | |
| 483 | } | |
| 484 | ||
| 485 | pub fn decode(this: IndexRegisterDisp) struct { | |
| 486 | index: Register, | |
| 487 | disp: u32, | |
| 488 | } { | |
| 489 | return .{ | |
| 490 | .index = @intToEnum(Register, this.index), | |
| 491 | .disp = this.disp, | |
| 492 | }; | |
| 493 | } | |
| 494 | }; | |
| 495 | ||
| 496 | /// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list | |
| 497 | /// instead of having two structs, one a superset of the other one? | |
| 498 | pub const IndexRegisterDispImm = struct { | |
| 499 | /// Index register to use with SIB-based encoding | |
| 500 | index: u32, | |
| 501 | ||
| 502 | /// Displacement value | |
| 503 | disp: u32, | |
| 504 | ||
| 505 | /// Immediate | |
| 506 | imm: u32, | |
| 507 | ||
| 508 | pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { | |
| 509 | return .{ | |
| 510 | .index = @enumToInt(index), | |
| 511 | .disp = disp, | |
| 512 | .imm = imm, | |
| 513 | }; | |
| 514 | } | |
| 515 | ||
| 516 | pub fn decode(this: IndexRegisterDispImm) struct { | |
| 517 | index: Register, | |
| 518 | disp: u32, | |
| 519 | imm: u32, | |
| 520 | } { | |
| 521 | return .{ | |
| 522 | .index = @intToEnum(Register, this.index), | |
| 523 | .disp = this.disp, | |
| 524 | .imm = this.imm, | |
| 525 | }; | |
| 526 | } | |
| 527 | }; | |
| 528 | ||
| 529 | /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers | |
| 530 | /// in a compact manner. | |
| 464 | 531 | pub const RegisterList = struct { |
| 465 | 532 | bitset: BitSet = BitSet.initEmpty(), |
| 466 | 533 |