authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-03 20:36:01+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-07 22:42:57+02:00
loge0167ae0e3f0e6c6a667b372b21c322d47ccd92d
tree27b8d51339716708ae8398f7683aa7f57e48e245
parent619d82234ecbb8a130e827e9de6f545288649a58

x86_64: allow for any index register in complex SIB encodings

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,7 +5932,6 @@ const InlineMemcpyOpts = struct {
5932 dest_stack_base: ?Register = null,5932 dest_stack_base: ?Register = null,
5933};5933};
59345934
5935/// Spills .rax and .rcx.
5936fn genInlineMemcpy(5935fn genInlineMemcpy(
5937 self: *Self,5936 self: *Self,
5938 dst_ptr: MCValue,5937 dst_ptr: MCValue,
...@@ -5940,19 +5939,6 @@ fn genInlineMemcpy(...@@ -5940,19 +5939,6 @@ fn genInlineMemcpy(
5940 len: MCValue,5939 len: MCValue,
5941 opts: InlineMemcpyOpts,5940 opts: InlineMemcpyOpts,
5942) InnerError!void {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 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|5942 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
5957 self.register_manager.lockReg(reg)5943 self.register_manager.lockReg(reg)
5958 else5944 else
...@@ -5965,7 +5951,13 @@ fn genInlineMemcpy(...@@ -5965,7 +5951,13 @@ fn genInlineMemcpy(
5965 null;5951 null;
5966 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);5952 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
59675953
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 switch (dst_ptr) {5961 switch (dst_ptr) {
5970 .memory,5962 .memory,
5971 .got_load,5963 .got_load,
...@@ -5998,10 +5990,7 @@ fn genInlineMemcpy(...@@ -5998,10 +5990,7 @@ fn genInlineMemcpy(
5998 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});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);
60035993
6004 const src_addr_reg = try self.register_manager.allocReg(null, gp);
6005 switch (src_ptr) {5994 switch (src_ptr) {
6006 .memory,5995 .memory,
6007 .got_load,5996 .got_load,
...@@ -6034,26 +6023,13 @@ fn genInlineMemcpy(...@@ -6034,26 +6023,13 @@ fn genInlineMemcpy(
6034 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});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();
60436026
6044 try self.genSetReg(Type.usize, count_reg, len);6027 try self.genSetReg(Type.usize, count_reg, len);
60456028
6046 // mov rcx, 06029 // mov index_reg, 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
6054 _ = try self.addInst(.{6030 _ = try self.addInst(.{
6055 .tag = .mov,6031 .tag = .mov,
6056 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6032 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
6057 .data = .{ .imm = 0 },6033 .data = .{ .imm = 0 },
6058 });6034 });
60596035
...@@ -6075,37 +6051,30 @@ fn genInlineMemcpy(...@@ -6075,37 +6051,30 @@ fn genInlineMemcpy(
6075 } },6051 } },
6076 });6052 });
60776053
6078 // mov tmp, [addr + rcx]6054 // mov tmp, [addr + index_reg]
6079 _ = try self.addInst(.{6055 _ = try self.addInst(.{
6080 .tag = .mov_scale_src,6056 .tag = .mov_scale_src,
6081 .ops = Mir.Inst.Ops.encode(.{6057 .ops = Mir.Inst.Ops.encode(.{
6082 .reg1 = tmp_reg.to8(),6058 .reg1 = tmp_reg.to8(),
6083 .reg2 = src_addr_reg,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 });
60876063
6088 // mov [stack_offset + rax], tmp6064 // mov [stack_offset + index_reg], tmp
6089 _ = try self.addInst(.{6065 _ = try self.addInst(.{
6090 .tag = .mov_scale_dst,6066 .tag = .mov_scale_dst,
6091 .ops = Mir.Inst.Ops.encode(.{6067 .ops = Mir.Inst.Ops.encode(.{
6092 .reg1 = dst_addr_reg,6068 .reg1 = dst_addr_reg,
6093 .reg2 = tmp_reg.to8(),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 });
60976073
6098 // add rcx, 16074 // add index_reg, 1
6099 _ = try self.addInst(.{6075 _ = try self.addInst(.{
6100 .tag = .add,6076 .tag = .add,
6101 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),6077 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
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 }),
6109 .data = .{ .imm = 1 },6078 .data = .{ .imm = 1 },
6110 });6079 });
61116080
...@@ -6127,7 +6096,6 @@ fn genInlineMemcpy(...@@ -6127,7 +6096,6 @@ fn genInlineMemcpy(
6127 try self.performReloc(loop_reloc);6096 try self.performReloc(loop_reloc);
6128}6097}
61296098
6130/// Spills .rax register.
6131fn genInlineMemset(6099fn genInlineMemset(
6132 self: *Self,6100 self: *Self,
6133 dst_ptr: MCValue,6101 dst_ptr: MCValue,
...@@ -6135,12 +6103,22 @@ fn genInlineMemset(...@@ -6135,12 +6103,22 @@ fn genInlineMemset(
6135 len: MCValue,6103 len: MCValue,
6136 opts: InlineMemcpyOpts,6104 opts: InlineMemcpyOpts,
6137) InnerError!void {6105) InnerError!void {
6138 // TODO preserve contents of .rax and then restore6106 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
6139 try self.register_manager.getReg(.rax, null);6107 self.register_manager.lockReg(reg)
6140 const rax_lock = self.register_manager.lockRegAssumeUnused(.rax);6108 else
6141 defer self.register_manager.unlockReg(rax_lock);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();
61426121
6143 const addr_reg = try self.register_manager.allocReg(null, gp);
6144 switch (dst_ptr) {6122 switch (dst_ptr) {
6145 .memory,6123 .memory,
6146 .got_load,6124 .got_load,
...@@ -6173,17 +6151,15 @@ fn genInlineMemset(...@@ -6173,17 +6151,15 @@ fn genInlineMemset(
6173 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});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);
61786154
6179 try self.genSetReg(Type.usize, .rax, len);6155 try self.genSetReg(Type.usize, index_reg, len);
6180 try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });6156 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });
61816157
6182 // loop:6158 // loop:
6183 // cmp rax, -16159 // cmp index_reg, -1
6184 const loop_start = try self.addInst(.{6160 const loop_start = try self.addInst(.{
6185 .tag = .cmp,6161 .tag = .cmp,
6186 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6162 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
6187 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },6163 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },
6188 });6164 });
61896165
...@@ -6202,24 +6178,20 @@ fn genInlineMemset(...@@ -6202,24 +6178,20 @@ fn genInlineMemset(
6202 if (x > math.maxInt(i32)) {6178 if (x > math.maxInt(i32)) {
6203 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});6179 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});
6204 }6180 }
6205 // mov byte ptr [rbp + rax + stack_offset], imm6181 // mov byte ptr [rbp + index_reg + stack_offset], imm
6206 const payload = try self.addExtra(Mir.ImmPair{
6207 .dest_off = 0,
6208 .operand = @truncate(u32, x),
6209 });
6210 _ = try self.addInst(.{6182 _ = try self.addInst(.{
6211 .tag = .mov_mem_index_imm,6183 .tag = .mov_mem_index_imm,
6212 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),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 else => return self.fail("TODO inline memset for value of type {}", .{value}),6188 else => return self.fail("TODO inline memset for value of type {}", .{value}),
6217 }6189 }
62186190
6219 // sub rax, 16191 // sub index_reg, 1
6220 _ = try self.addInst(.{6192 _ = try self.addInst(.{
6221 .tag = .sub,6193 .tag = .sub,
6222 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6194 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
6223 .data = .{ .imm = 1 },6195 .data = .{ .imm = 1 },
6224 });6196 });
62256197
src/arch/x86_64/Emit.zig+39-30
...@@ -615,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 {...@@ -615,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 {
615fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {615fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
616 const ops = emit.mir.instructions.items(.ops)[inst].decode();616 const ops = emit.mir.instructions.items(.ops)[inst].decode();
617 const scale = ops.flags;617 const scale = ops.flags;
618 const imm = emit.mir.instructions.items(.data)[inst].imm;618 const payload = emit.mir.instructions.items(.data)[inst].payload;
619 // OP reg1, [reg2 + scale*rcx + imm32]619 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
620 // OP reg1, [reg2 + scale*index + imm32]
620 const scale_index = ScaleIndex{621 const scale_index = ScaleIndex{
621 .scale = scale,622 .scale = scale,
622 .index = .rcx,623 .index = index_reg_disp.index,
623 };624 };
624 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{625 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
625 .disp = imm,626 .disp = index_reg_disp.disp,
626 .base = ops.reg2,627 .base = ops.reg2,
627 .scale_index = scale_index,628 .scale_index = scale_index,
628 }), emit.code);629 }), emit.code);
...@@ -631,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -631,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
631fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {632fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
632 const ops = emit.mir.instructions.items(.ops)[inst].decode();633 const ops = emit.mir.instructions.items(.ops)[inst].decode();
633 const scale = ops.flags;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 const scale_index = ScaleIndex{637 const scale_index = ScaleIndex{
636 .scale = scale,638 .scale = scale,
637 .index = .rax,639 .index = index_reg_disp.index,
638 };640 };
639 if (ops.reg2 == .none) {641 assert(ops.reg2 != .none);
640 // OP qword ptr [reg1 + scale*rax + 0], imm32642 // OP [reg1 + scale*index + imm32], reg2
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
648 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{643 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
649 .disp = imm,644 .disp = index_reg_disp.disp,
650 .base = ops.reg1,645 .base = ops.reg1,
651 .scale_index = scale_index,646 .scale_index = scale_index,
652 }), ops.reg2, emit.code);647 }), ops.reg2, emit.code);
...@@ -656,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -656,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
656 const ops = emit.mir.instructions.items(.ops)[inst].decode();651 const ops = emit.mir.instructions.items(.ops)[inst].decode();
657 const scale = ops.flags;652 const scale = ops.flags;
658 const payload = emit.mir.instructions.items(.data)[inst].payload;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 const scale_index = ScaleIndex{655 const scale_index = ScaleIndex{
661 .scale = scale,656 .scale = scale,
662 .index = .rax,657 .index = index_reg_disp_imm.index,
663 };658 };
664 // OP qword ptr [reg1 + scale*rax + imm32], imm32659 // OP qword ptr [reg1 + scale*index + imm32], imm32
665 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{660 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
666 .disp = imm_pair.dest_off,661 .disp = index_reg_disp_imm.disp,
667 .base = ops.reg1,662 .base = ops.reg1,
668 .scale_index = scale_index,663 .scale_index = scale_index,
669 }), imm_pair.operand, emit.code);664 }), index_reg_disp_imm.imm, emit.code);
670}665}
671666
672fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {667fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
673 const ops = emit.mir.instructions.items(.ops)[inst].decode();668 const ops = emit.mir.instructions.items(.ops)[inst].decode();
674 assert(ops.reg2 == .none);669 assert(ops.reg2 == .none);
675 const payload = emit.mir.instructions.items(.data)[inst].payload;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 const ptr_size: Memory.PtrSize = switch (ops.flags) {672 const ptr_size: Memory.PtrSize = switch (ops.flags) {
678 0b00 => .byte_ptr,673 0b00 => .byte_ptr,
679 0b01 => .word_ptr,674 0b01 => .word_ptr,
...@@ -682,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v...@@ -682,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v
682 };677 };
683 const scale_index = ScaleIndex{678 const scale_index = ScaleIndex{
684 .scale = 0,679 .scale = 0,
685 .index = .rax,680 .index = index_reg_disp_imm.index,
686 };681 };
687 // OP ptr [reg1 + rax*1 + imm32], imm32682 // OP ptr [reg1 + index + imm32], imm32
688 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{683 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
689 .disp = imm_pair.dest_off,684 .disp = index_reg_disp_imm.disp,
690 .base = ops.reg1,685 .base = ops.reg1,
691 .scale_index = scale_index,686 .scale_index = scale_index,
692 }), imm_pair.operand, emit.code);687 }), index_reg_disp_imm.imm, emit.code);
693}688}
694689
695fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {690fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -957,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -957,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
957 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);952 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);
958 },953 },
959 0b10 => {954 0b10 => {
960 // lea reg, [rbp + rcx + imm32]955 // lea reg, [rbp + index + imm32]
961 const imm = emit.mir.instructions.items(.data)[inst].imm;956 const payload = emit.mir.instructions.items(.data)[inst].payload;
957 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
962 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;958 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
963 const scale_index = ScaleIndex{959 const scale_index = ScaleIndex{
964 .scale = 0,960 .scale = 0,
965 .index = .rcx,961 .index = index_reg_disp.index,
966 };962 };
967 return lowerToRmEnc(963 return lowerToRmEnc(
968 .lea,964 .lea,
969 ops.reg1,965 ops.reg1,
970 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{966 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
971 .disp = imm,967 .disp = index_reg_disp.disp,
972 .base = src_reg,968 .base = src_reg,
973 .scale_index = scale_index,969 .scale_index = scale_index,
974 }),970 }),
...@@ -2255,6 +2251,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st...@@ -2255,6 +2251,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st
2255 encoder.rex(.{2251 encoder.rex(.{
2256 .w = wide,2252 .w = wide,
2257 .b = base.isExtended(),2253 .b = base.isExtended(),
2254 .x = if (mem_op.scale_index) |si| si.index.isExtended() else false,
2258 });2255 });
2259 }2256 }
2260 opc.encode(encoder);2257 opc.encode(encoder);
...@@ -2360,10 +2357,12 @@ fn lowerToMiXEnc(...@@ -2360,10 +2357,12 @@ fn lowerToMiXEnc(
2360 encoder.rex(.{2357 encoder.rex(.{
2361 .w = dst_mem.ptr_size == .qword_ptr,2358 .w = dst_mem.ptr_size == .qword_ptr,
2362 .b = base.isExtended(),2359 .b = base.isExtended(),
2360 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2363 });2361 });
2364 } else {2362 } else {
2365 encoder.rex(.{2363 encoder.rex(.{
2366 .w = dst_mem.ptr_size == .qword_ptr,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 opc.encode(encoder);2368 opc.encode(encoder);
...@@ -2415,11 +2414,13 @@ fn lowerToRmEnc(...@@ -2415,11 +2414,13 @@ fn lowerToRmEnc(
2415 .w = setRexWRegister(reg),2414 .w = setRexWRegister(reg),
2416 .r = reg.isExtended(),2415 .r = reg.isExtended(),
2417 .b = base.isExtended(),2416 .b = base.isExtended(),
2417 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2418 });2418 });
2419 } else {2419 } else {
2420 encoder.rex(.{2420 encoder.rex(.{
2421 .w = setRexWRegister(reg),2421 .w = setRexWRegister(reg),
2422 .r = reg.isExtended(),2422 .r = reg.isExtended(),
2423 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2423 });2424 });
2424 }2425 }
2425 opc.encode(encoder);2426 opc.encode(encoder);
...@@ -2460,11 +2461,13 @@ fn lowerToMrEnc(...@@ -2460,11 +2461,13 @@ fn lowerToMrEnc(
2460 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),2461 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2461 .r = reg.isExtended(),2462 .r = reg.isExtended(),
2462 .b = base.isExtended(),2463 .b = base.isExtended(),
2464 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2463 });2465 });
2464 } else {2466 } else {
2465 encoder.rex(.{2467 encoder.rex(.{
2466 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),2468 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2467 .r = reg.isExtended(),2469 .r = reg.isExtended(),
2470 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2468 });2471 });
2469 }2472 }
2470 opc.encode(encoder);2473 opc.encode(encoder);
...@@ -2504,11 +2507,13 @@ fn lowerToRmiEnc(...@@ -2504,11 +2507,13 @@ fn lowerToRmiEnc(
2504 .w = setRexWRegister(reg),2507 .w = setRexWRegister(reg),
2505 .r = reg.isExtended(),2508 .r = reg.isExtended(),
2506 .b = base.isExtended(),2509 .b = base.isExtended(),
2510 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2507 });2511 });
2508 } else {2512 } else {
2509 encoder.rex(.{2513 encoder.rex(.{
2510 .w = setRexWRegister(reg),2514 .w = setRexWRegister(reg),
2511 .r = reg.isExtended(),2515 .r = reg.isExtended(),
2516 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2512 });2517 });
2513 }2518 }
2514 opc.encode(encoder);2519 opc.encode(encoder);
...@@ -2545,10 +2550,12 @@ fn lowerToVmEnc(...@@ -2545,10 +2550,12 @@ fn lowerToVmEnc(
2545 vex.rex(.{2550 vex.rex(.{
2546 .r = reg.isExtended(),2551 .r = reg.isExtended(),
2547 .b = base.isExtended(),2552 .b = base.isExtended(),
2553 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2548 });2554 });
2549 } else {2555 } else {
2550 vex.rex(.{2556 vex.rex(.{
2551 .r = reg.isExtended(),2557 .r = reg.isExtended(),
2558 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2552 });2559 });
2553 }2560 }
2554 encoder.vex(enc.prefix);2561 encoder.vex(enc.prefix);
...@@ -2585,10 +2592,12 @@ fn lowerToMvEnc(...@@ -2585,10 +2592,12 @@ fn lowerToMvEnc(
2585 vex.rex(.{2592 vex.rex(.{
2586 .r = reg.isExtended(),2593 .r = reg.isExtended(),
2587 .b = base.isExtended(),2594 .b = base.isExtended(),
2595 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2588 });2596 });
2589 } else {2597 } else {
2590 vex.rex(.{2598 vex.rex(.{
2591 .r = reg.isExtended(),2599 .r = reg.isExtended(),
2600 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2592 });2601 });
2593 }2602 }
2594 encoder.vex(enc.prefix);2603 encoder.vex(enc.prefix);
src/arch/x86_64/Mir.zig+77-10
...@@ -44,25 +44,28 @@ pub const Inst = struct {...@@ -44,25 +44,28 @@ pub const Inst = struct {
44 /// 0b01 word ptr [reg1 + imm32], imm1644 /// 0b01 word ptr [reg1 + imm32], imm16
45 /// 0b10 dword ptr [reg1 + imm32], imm3245 /// 0b10 dword ptr [reg1 + imm32], imm32
46 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)46 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)
47 /// Notes:
48 /// * Uses `ImmPair` as payload
47 adc_mem_imm,49 adc_mem_imm,
4850
49 /// form: reg1, [reg2 + scale*rcx + imm32]51 /// form: reg1, [reg2 + scale*index + imm32]
50 /// ops flags scale52 /// ops flags scale
51 /// 0b00 153 /// 0b00 1
52 /// 0b01 254 /// 0b01 2
53 /// 0b10 455 /// 0b10 4
54 /// 0b11 856 /// 0b11 8
57 /// Notes:
58 /// * Uses `IndexRegisterDisp` as payload
55 adc_scale_src,59 adc_scale_src,
5660
57 /// form: [reg1 + scale*rax + imm32], reg261 /// form: [reg1 + scale*index + imm32], reg2
58 /// form: [reg1 + scale*rax + 0], imm32
59 /// ops flags scale62 /// ops flags scale
60 /// 0b00 163 /// 0b00 1
61 /// 0b01 264 /// 0b01 2
62 /// 0b10 465 /// 0b10 4
63 /// 0b11 866 /// 0b11 8
64 /// Notes:67 /// Notes:
65 /// * If reg2 is `none` then it means Data field `imm` is used as the immediate.68 /// * Uses `IndexRegisterDisp` payload.
66 adc_scale_dst,69 adc_scale_dst,
6770
68 /// form: [reg1 + scale*rax + imm32], imm3271 /// form: [reg1 + scale*rax + imm32], imm32
...@@ -72,14 +75,16 @@ pub const Inst = struct {...@@ -72,14 +75,16 @@ pub const Inst = struct {
72 /// 0b10 475 /// 0b10 4
73 /// 0b11 876 /// 0b11 8
74 /// Notes:77 /// Notes:
75 /// * Data field `payload` points at `ImmPair`.78 /// * Uses `IndexRegisterDispImm` payload.
76 adc_scale_imm,79 adc_scale_imm,
7780
78 /// ops flags: form:81 /// ops flags: form:
79 /// 0b00 byte ptr [reg1 + rax + imm32], imm882 /// 0b00 byte ptr [reg1 + index + imm32], imm8
80 /// 0b01 word ptr [reg1 + rax + imm32], imm1683 /// 0b01 word ptr [reg1 + index + imm32], imm16
81 /// 0b10 dword ptr [reg1 + rax + imm32], imm3284 /// 0b10 dword ptr [reg1 + index + imm32], imm32
82 /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64)85 /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64)
86 /// Notes:
87 /// * Uses `IndexRegisterDispImm` payload.
83 adc_mem_index_imm,88 adc_mem_index_imm,
8489
85 // The following instructions all have the same encoding as `adc`.90 // The following instructions all have the same encoding as `adc`.
...@@ -174,7 +179,9 @@ pub const Inst = struct {...@@ -174,7 +179,9 @@ pub const Inst = struct {
174 /// 0b00 reg1, [reg2 + imm32]179 /// 0b00 reg1, [reg2 + imm32]
175 /// 0b00 reg1, [ds:imm32]180 /// 0b00 reg1, [ds:imm32]
176 /// 0b01 reg1, [rip + imm32]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 lea,185 lea,
179186
180 /// ops flags: form:187 /// ops flags: form:
...@@ -461,6 +468,66 @@ pub const Inst = struct {...@@ -461,6 +468,66 @@ pub const Inst = struct {
461 }468 }
462};469};
463470
471pub 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?
498pub 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.
464pub const RegisterList = struct {531pub const RegisterList = struct {
465 bitset: BitSet = BitSet.initEmpty(),532 bitset: BitSet = BitSet.initEmpty(),
466533