| ... | ... | @@ -581,6 +581,7 @@ inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 { |
| 581 | 581 | .sbb => 0x1b, |
| 582 | 582 | .cmp => 0x3b, |
| 583 | 583 | .mov => 0x8b, |
| 584 | .lea => 0x8d, |
| 584 | 585 | else => unreachable, |
| 585 | 586 | }, |
| 586 | 587 | .oi => return switch (tag) { |
| ... | ... | @@ -621,6 +622,7 @@ const ScaleIndexBase = struct { |
| 621 | 622 | |
| 622 | 623 | const Memory = struct { |
| 623 | 624 | reg: ?Register, |
| 625 | rip: bool = false, |
| 624 | 626 | disp: i32, |
| 625 | 627 | sib: ?ScaleIndexBase = null, |
| 626 | 628 | }; |
| ... | ... | @@ -641,6 +643,16 @@ const RegisterOrMemory = union(enum) { |
| 641 | 643 | }, |
| 642 | 644 | }; |
| 643 | 645 | } |
| 646 | |
| 647 | fn rip(disp: i32) RegisterOrMemory { |
| 648 | return .{ |
| 649 | .memory = .{ |
| 650 | .reg = null, |
| 651 | .rip = true, |
| 652 | .disp = disp, |
| 653 | }, |
| 654 | }; |
| 655 | } |
| 644 | 656 | }; |
| 645 | 657 | |
| 646 | 658 | fn lowerToTdEnc( |
| ... | ... | @@ -813,8 +825,12 @@ fn lowerToMiEnc( |
| 813 | 825 | } |
| 814 | 826 | } else { |
| 815 | 827 | encoder.opcode_1byte(opc); |
| 816 | | encoder.modRm_SIBDisp0(modrm_ext); |
| 817 | | encoder.sib_disp32(); |
| 828 | if (dst_mem.rip) { |
| 829 | encoder.modRm_RIPDisp32(modrm_ext); |
| 830 | } else { |
| 831 | encoder.modRm_SIBDisp0(modrm_ext); |
| 832 | encoder.sib_disp32(); |
| 833 | } |
| 818 | 834 | encoder.disp32(dst_mem.disp); |
| 819 | 835 | } |
| 820 | 836 | encoder.imm32(imm); |
| ... | ... | @@ -880,8 +896,12 @@ fn lowerToRmEnc( |
| 880 | 896 | .r = reg.isExtended(), |
| 881 | 897 | }); |
| 882 | 898 | encoder.opcode_1byte(opc); |
| 883 | | encoder.modRm_SIBDisp0(reg.lowId()); |
| 884 | | encoder.sib_disp32(); |
| 899 | if (src_mem.rip) { |
| 900 | encoder.modRm_RIPDisp32(reg.lowId()); |
| 901 | } else { |
| 902 | encoder.modRm_SIBDisp0(reg.lowId()); |
| 903 | encoder.sib_disp32(); |
| 904 | } |
| 885 | 905 | encoder.disp32(src_mem.disp); |
| 886 | 906 | } |
| 887 | 907 | }, |
| ... | ... | @@ -950,8 +970,12 @@ fn lowerToMrEnc( |
| 950 | 970 | .r = reg.isExtended(), |
| 951 | 971 | }); |
| 952 | 972 | encoder.opcode_1byte(opc); |
| 953 | | encoder.modRm_SIBDisp0(reg.lowId()); |
| 954 | | encoder.sib_disp32(); |
| 973 | if (dst_mem.rip) { |
| 974 | encoder.modRm_RIPDisp32(reg.lowId()); |
| 975 | } else { |
| 976 | encoder.modRm_SIBDisp0(reg.lowId()); |
| 977 | encoder.sib_disp32(); |
| 978 | } |
| 955 | 979 | encoder.disp32(dst_mem.disp); |
| 956 | 980 | } |
| 957 | 981 | }, |
| ... | ... | @@ -1206,37 +1230,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1206 | 1230 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1207 | 1231 | assert(ops.flags == 0b01); |
| 1208 | 1232 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 1209 | | |
| 1210 | | if (imm == 0) { |
| 1211 | | const encoder = try Encoder.init(emit.code, 3); |
| 1212 | | encoder.rex(.{ |
| 1213 | | .w = ops.reg1.size() == 64, |
| 1214 | | .r = ops.reg1.isExtended(), |
| 1215 | | .b = ops.reg2.isExtended(), |
| 1216 | | }); |
| 1217 | | encoder.opcode_1byte(0x8d); |
| 1218 | | encoder.modRm_indirectDisp0(ops.reg1.lowId(), ops.reg2.lowId()); |
| 1219 | | } else if (imm <= math.maxInt(i8)) { |
| 1220 | | const encoder = try Encoder.init(emit.code, 4); |
| 1221 | | encoder.rex(.{ |
| 1222 | | .w = ops.reg1.size() == 64, |
| 1223 | | .r = ops.reg1.isExtended(), |
| 1224 | | .b = ops.reg2.isExtended(), |
| 1225 | | }); |
| 1226 | | encoder.opcode_1byte(0x8d); |
| 1227 | | encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId()); |
| 1228 | | encoder.disp8(@intCast(i8, imm)); |
| 1229 | | } else { |
| 1230 | | const encoder = try Encoder.init(emit.code, 7); |
| 1231 | | encoder.rex(.{ |
| 1232 | | .w = ops.reg1.size() == 64, |
| 1233 | | .r = ops.reg1.isExtended(), |
| 1234 | | .b = ops.reg2.isExtended(), |
| 1235 | | }); |
| 1236 | | encoder.opcode_1byte(0x8d); |
| 1237 | | encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId()); |
| 1238 | | encoder.disp32(imm); |
| 1239 | | } |
| 1233 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ops.reg2, imm), emit.code); |
| 1240 | 1234 | } |
| 1241 | 1235 | |
| 1242 | 1236 | fn mirLeaRip(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| ... | ... | @@ -1244,26 +1238,22 @@ fn mirLeaRip(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1244 | 1238 | assert(tag == .lea_rip); |
| 1245 | 1239 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1246 | 1240 | const start_offset = emit.code.items.len; |
| 1247 | | const encoder = try Encoder.init(emit.code, 7); |
| 1248 | | encoder.rex(.{ |
| 1249 | | .w = ops.reg1.size() == 64, |
| 1250 | | .r = ops.reg1.isExtended(), |
| 1251 | | }); |
| 1252 | | encoder.opcode_1byte(0x8d); |
| 1253 | | encoder.modRm_RIPDisp32(ops.reg1.lowId()); |
| 1241 | try lowerToRmEnc(.lea, ops.reg1, RegisterOrMemory.rip(0), emit.code); |
| 1254 | 1242 | const end_offset = emit.code.items.len; |
| 1255 | 1243 | if (@truncate(u1, ops.flags) == 0b0) { |
| 1244 | // Backpatch the displacement |
| 1245 | // TODO figure out if this can be simplified |
| 1256 | 1246 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 1257 | 1247 | const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); |
| 1258 | | encoder.disp32(@intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset + 4))); |
| 1248 | const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); |
| 1249 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); |
| 1259 | 1250 | } else { |
| 1260 | 1251 | const got_entry = emit.mir.instructions.items(.data)[inst].got_entry; |
| 1261 | | encoder.disp32(0); |
| 1262 | 1252 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 1263 | 1253 | // TODO I think the reloc might be in the wrong place. |
| 1264 | 1254 | const decl = macho_file.active_decl.?; |
| 1265 | 1255 | try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ |
| 1266 | | .offset = @intCast(u32, end_offset), |
| 1256 | .offset = @intCast(u32, end_offset - 4), |
| 1267 | 1257 | .target = .{ .local = got_entry }, |
| 1268 | 1258 | .addend = 0, |
| 1269 | 1259 | .subtractor = null, |
| ... | ... | @@ -1530,6 +1520,12 @@ test "lower MI encoding" { |
| 1530 | 1520 | code.emitted(), |
| 1531 | 1521 | "and dword ptr [r12 + 0x10000000], 0x10", |
| 1532 | 1522 | ); |
| 1523 | try lowerToMiEnc(.mov, RegisterOrMemory.rip(0x10), 0x10, code.buffer()); |
| 1524 | try expectEqualHexStrings( |
| 1525 | "\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", |
| 1526 | code.emitted(), |
| 1527 | "mov [rip + 0x10], 0x10", |
| 1528 | ); |
| 1533 | 1529 | } |
| 1534 | 1530 | |
| 1535 | 1531 | test "lower RM encoding" { |
| ... | ... | @@ -1565,6 +1561,8 @@ test "lower RM encoding" { |
| 1565 | 1561 | ); |
| 1566 | 1562 | try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.rbp, -4), code.buffer()); |
| 1567 | 1563 | try expectEqualHexStrings("\x48\x8B\x45\xFC", code.emitted(), "mov rax, qword ptr [rbp - 4]"); |
| 1564 | try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(0x10), code.buffer()); |
| 1565 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", code.emitted(), "lea rax, [rip + 0x10]"); |
| 1568 | 1566 | } |
| 1569 | 1567 | |
| 1570 | 1568 | test "lower MR encoding" { |
| ... | ... | @@ -1592,6 +1590,8 @@ test "lower MR encoding" { |
| 1592 | 1590 | code.emitted(), |
| 1593 | 1591 | "sub qword ptr [r11 + 0x10000000], r12", |
| 1594 | 1592 | ); |
| 1593 | try lowerToMrEnc(.mov, RegisterOrMemory.rip(0x10), .r12, code.buffer()); |
| 1594 | try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", code.emitted(), "mov qword ptr [rip + 0x10], r12"); |
| 1595 | 1595 | } |
| 1596 | 1596 | |
| 1597 | 1597 | test "lower OI encoding" { |