authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-23 16:39:51-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-23 16:39:51-08:00
log0049c7180f6b8df597c045d02189348f3be0cb0e
treea015ea0254b44bc2aff3eac0e449a2fe5f5a70fc
parent303bad9989866373042df9e80722185608bf1147
parent4c119866507b244dc0658b5cb43e20295f1888d7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10394 from ziglang/stage2-x86_64-mir-intel-syntax

stage2: rewrite MIR -> Isel layer for x86_64

3 files changed, 1313 insertions(+), 584 deletions(-)

src/arch/x86_64/CodeGen.zig+22-21
......@@ -275,7 +275,9 @@ pub fn generate(
275275 .stack_align = undefined,
276276 .end_di_line = module_fn.rbrace_line,
277277 .end_di_column = module_fn.rbrace_column,
278 .mir_to_air_map = if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator) else {},
278 .mir_to_air_map = if (builtin.mode == .Debug)
279 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
280 else {},
279281 };
280282 defer function.stack.deinit(bin_file.allocator);
281283 defer function.blocks.deinit(bin_file.allocator);
......@@ -386,8 +388,8 @@ fn gen(self: *Self) InnerError!void {
386388 _ = try self.addInst(.{
387389 .tag = .mov,
388390 .ops = (Mir.Ops{
389 .reg1 = .rsp,
390 .reg2 = .rbp,
391 .reg1 = .rbp,
392 .reg2 = .rsp,
391393 }).encode(),
392394 .data = undefined,
393395 });
......@@ -1632,18 +1634,18 @@ fn genBinMathOpMir(
16321634 _ = try self.addInst(.{
16331635 .tag = mir_tag,
16341636 .ops = (Mir.Ops{
1635 .reg1 = src_reg,
1636 .reg2 = dst_reg,
1637 .flags = 0b11,
1637 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
1638 .reg2 = src_reg,
16381639 }).encode(),
16391640 .data = undefined,
16401641 });
16411642 },
16421643 .immediate => |imm| {
1644 // TODO I am not quite sure why we need to set the size of the register here...
16431645 _ = try self.addInst(.{
16441646 .tag = mir_tag,
16451647 .ops = (Mir.Ops{
1646 .reg1 = dst_reg,
1648 .reg1 = registerAlias(dst_reg, 4),
16471649 }).encode(),
16481650 .data = .{ .imm = @intCast(i32, imm) },
16491651 });
......@@ -1661,7 +1663,7 @@ fn genBinMathOpMir(
16611663 .tag = mir_tag,
16621664 .ops = (Mir.Ops{
16631665 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
1664 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
1666 .reg2 = .rbp,
16651667 .flags = 0b01,
16661668 }).encode(),
16671669 .data = .{ .imm = -@intCast(i32, adj_off) },
......@@ -1691,8 +1693,8 @@ fn genBinMathOpMir(
16911693 _ = try self.addInst(.{
16921694 .tag = mir_tag,
16931695 .ops = (Mir.Ops{
1694 .reg1 = registerAlias(src_reg, @intCast(u32, abi_size)),
1695 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
1696 .reg1 = .rbp,
1697 .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
16961698 .flags = 0b10,
16971699 }).encode(),
16981700 .data = .{ .imm = -@intCast(i32, adj_off) },
......@@ -1741,7 +1743,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
17411743 _ = try self.addInst(.{
17421744 .tag = .imul_complex,
17431745 .ops = (Mir.Ops{
1744 .reg1 = dst_reg,
1746 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
17451747 .reg2 = src_reg,
17461748 }).encode(),
17471749 .data = undefined,
......@@ -1790,7 +1792,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
17901792 _ = try self.addInst(.{
17911793 .tag = .imul_complex,
17921794 .ops = (Mir.Ops{
1793 .reg1 = dst_reg,
1795 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
17941796 .reg2 = src_reg,
17951797 }).encode(),
17961798 .data = undefined,
......@@ -2868,8 +2870,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
28682870 _ = try self.addInst(.{
28692871 .tag = .mov,
28702872 .ops = (Mir.Ops{
2871 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
2872 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
2873 .reg1 = .rbp,
2874 .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
28732875 .flags = 0b10,
28742876 }).encode(),
28752877 .data = .{ .imm = -@intCast(i32, adj_off) },
......@@ -2926,7 +2928,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
29262928 _ = try self.addInst(.{
29272929 .tag = tag,
29282930 .ops = (Mir.Ops{
2929 .reg1 = reg,
2931 .reg1 = reg.to8(),
29302932 .flags = flags,
29312933 }).encode(),
29322934 .data = undefined,
......@@ -2952,10 +2954,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
29522954 }
29532955 if (x <= math.maxInt(i32)) {
29542956 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
2957 // TODO I am not quite sure why we need to set the size of the register here...
29552958 _ = try self.addInst(.{
29562959 .tag = .mov,
29572960 .ops = (Mir.Ops{
2958 .reg1 = reg,
2961 .reg1 = registerAlias(reg, 4),
29592962 }).encode(),
29602963 .data = .{ .imm = @intCast(i32, x) },
29612964 });
......@@ -2996,9 +2999,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
29962999 _ = try self.addInst(.{
29973000 .tag = .mov,
29983001 .ops = (Mir.Ops{
2999 .reg1 = reg,
3002 .reg1 = registerAlias(reg, @divExact(src_reg.size(), 8)),
30003003 .reg2 = src_reg,
3001 .flags = 0b11,
30023004 }).encode(),
30033005 .data = undefined,
30043006 });
......@@ -3085,15 +3087,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
30853087 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
30863088 return self.fail("stack offset too large", .{});
30873089 }
3088 const ioff = -@intCast(i32, off);
30893090 _ = try self.addInst(.{
30903091 .tag = .mov,
30913092 .ops = (Mir.Ops{
30923093 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
3093 .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)),
3094 .reg2 = .rbp,
30943095 .flags = 0b01,
30953096 }).encode(),
3096 .data = .{ .imm = ioff },
3097 .data = .{ .imm = -@intCast(i32, off) },
30973098 });
30983099 },
30993100 }
src/arch/x86_64/Emit.zig+1290-563
......@@ -11,8 +11,10 @@ const link = @import("../../link.zig");
1111const log = std.log.scoped(.codegen);
1212const math = std.math;
1313const mem = std.mem;
14const testing = std.testing;
1415
1516const Air = @import("../../Air.zig");
17const Allocator = mem.Allocator;
1618const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
1719const DW = std.dwarf;
1820const Encoder = bits.Encoder;
......@@ -72,6 +74,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
7274 .@"or" => try emit.mirArith(.@"or", inst),
7375 .sbb => try emit.mirArith(.sbb, inst),
7476 .cmp => try emit.mirArith(.cmp, inst),
77 .mov => try emit.mirArith(.mov, inst),
7578
7679 .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),
7780 .add_scale_src => try emit.mirArithScaleSrc(.add, inst),
......@@ -81,6 +84,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
8184 .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),
8285 .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),
8386 .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),
87 .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),
8488
8589 .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),
8690 .add_scale_dst => try emit.mirArithScaleDst(.add, inst),
......@@ -90,6 +94,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
9094 .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),
9195 .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst),
9296 .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst),
97 .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst),
9398
9499 .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst),
95100 .add_scale_imm => try emit.mirArithScaleImm(.add, inst),
......@@ -99,14 +104,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
99104 .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst),
100105 .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst),
101106 .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst),
102
103 // Even though MOV is technically not an arithmetic op,
104 // its structure can be represented using the same set of
105 // opcode primitives.
106 .mov => try emit.mirArith(.mov, inst),
107 .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),
108 .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst),
109107 .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst),
108
110109 .movabs => try emit.mirMovabs(inst),
111110
112111 .lea => try emit.mirLea(inst),
......@@ -117,16 +116,18 @@ pub fn emitMir(emit: *Emit) InnerError!void {
117116 .push => try emit.mirPushPop(.push, inst),
118117 .pop => try emit.mirPushPop(.pop, inst),
119118
120 .jmp => try emit.mirJmpCall(.jmp, inst),
121 .call => try emit.mirJmpCall(.call, inst),
119 .jmp => try emit.mirJmpCall(.jmp_near, inst),
120 .call => try emit.mirJmpCall(.call_near, inst),
122121
123 .cond_jmp_greater_less => try emit.mirCondJmp(.cond_jmp_greater_less, inst),
124 .cond_jmp_above_below => try emit.mirCondJmp(.cond_jmp_above_below, inst),
125 .cond_jmp_eq_ne => try emit.mirCondJmp(.cond_jmp_eq_ne, inst),
122 .cond_jmp_greater_less,
123 .cond_jmp_above_below,
124 .cond_jmp_eq_ne,
125 => try emit.mirCondJmp(tag, inst),
126126
127 .cond_set_byte_greater_less => try emit.mirCondSetByte(.cond_set_byte_greater_less, inst),
128 .cond_set_byte_above_below => try emit.mirCondSetByte(.cond_set_byte_above_below, inst),
129 .cond_set_byte_eq_ne => try emit.mirCondSetByte(.cond_set_byte_eq_ne, inst),
127 .cond_set_byte_greater_less,
128 .cond_set_byte_above_below,
129 .cond_set_byte_eq_ne,
130 => try emit.mirCondSetByte(tag, inst),
130131
131132 .ret => try emit.mirRet(inst),
132133
......@@ -184,95 +185,45 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
184185}
185186
186187fn mirBrk(emit: *Emit) InnerError!void {
187 const encoder = try Encoder.init(emit.code, 1);
188 encoder.opcode_1byte(0xcc);
188 return lowerToZoEnc(.brk, emit.code);
189189}
190190
191191fn mirNop(emit: *Emit) InnerError!void {
192 const encoder = try Encoder.init(emit.code, 1);
193 encoder.opcode_1byte(0x90);
192 return lowerToZoEnc(.nop, emit.code);
194193}
195194
196195fn mirSyscall(emit: *Emit) InnerError!void {
197 const encoder = try Encoder.init(emit.code, 2);
198 encoder.opcode_2byte(0x0f, 0x05);
196 return lowerToZoEnc(.syscall, emit.code);
199197}
200198
201fn mirPushPop(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
199fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
202200 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
203201 switch (ops.flags) {
204202 0b00 => {
205203 // PUSH/POP reg
206 const opc: u8 = switch (tag) {
207 .push => 0x50,
208 .pop => 0x58,
209 else => unreachable,
210 };
211 const encoder = try Encoder.init(emit.code, 2);
212 encoder.rex(.{
213 .b = ops.reg1.isExtended(),
214 });
215 encoder.opcode_withReg(opc, ops.reg1.lowId());
204 return lowerToOEnc(tag, ops.reg1, emit.code);
216205 },
217206 0b01 => {
218207 // PUSH/POP r/m64
219208 const imm = emit.mir.instructions.items(.data)[inst].imm;
220 const opc: u8 = switch (tag) {
221 .push => 0xff,
222 .pop => 0x8f,
223 else => unreachable,
224 };
225 const modrm_ext: u3 = switch (tag) {
226 .push => 0x6,
227 .pop => 0x0,
228 else => unreachable,
229 };
230 const encoder = try Encoder.init(emit.code, 6);
231 encoder.opcode_1byte(opc);
232 if (math.cast(i8, imm)) |imm_i8| {
233 encoder.modRm_indirectDisp8(modrm_ext, ops.reg1.lowId());
234 encoder.imm8(@intCast(i8, imm_i8));
235 } else |_| {
236 encoder.modRm_indirectDisp32(modrm_ext, ops.reg1.lowId());
237 encoder.imm32(imm);
238 }
209 return lowerToMEnc(tag, RegisterOrMemory.mem(ops.reg1, imm), emit.code);
239210 },
240211 0b10 => {
241212 // PUSH imm32
242213 assert(tag == .push);
243214 const imm = emit.mir.instructions.items(.data)[inst].imm;
244 const opc: u8 = if (imm <= math.maxInt(i8)) 0x6a else 0x6b;
245 const encoder = try Encoder.init(emit.code, 2);
246 encoder.opcode_1byte(opc);
247 if (imm <= math.maxInt(i8)) {
248 encoder.imm8(@intCast(i8, imm));
249 } else if (imm <= math.maxInt(i16)) {
250 encoder.imm16(@intCast(i16, imm));
251 } else {
252 encoder.imm32(imm);
253 }
215 return lowerToIEnc(.push, imm, emit.code);
254216 },
255217 0b11 => unreachable,
256218 }
257219}
258fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
220fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
259221 const callee_preserved_regs = bits.callee_preserved_regs;
260 // PUSH/POP reg
261 const opc: u8 = switch (tag) {
262 .push => 0x50,
263 .pop => 0x58,
264 else => unreachable,
265 };
266
267222 const regs = emit.mir.instructions.items(.data)[inst].regs_to_push_or_pop;
268223 if (tag == .push) {
269224 for (callee_preserved_regs) |reg, i| {
270225 if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
271 const encoder = try Encoder.init(emit.code, 2);
272 encoder.rex(.{
273 .b = reg.isExtended(),
274 });
275 encoder.opcode_withReg(opc, reg.lowId());
226 try lowerToOEnc(.push, reg, emit.code);
276227 }
277228 } else {
278229 // pop in the reverse direction
......@@ -280,201 +231,89 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Mir.Inst.Tag, inst: M
280231 while (i > 0) : (i -= 1) {
281232 const reg = callee_preserved_regs[i - 1];
282233 if ((regs >> @intCast(u5, i - 1)) & 1 == 0) continue;
283 const encoder = try Encoder.init(emit.code, 2);
284 encoder.rex(.{
285 .b = reg.isExtended(),
286 });
287 encoder.opcode_withReg(opc, reg.lowId());
234 try lowerToOEnc(.pop, reg, emit.code);
288235 }
289236 }
290237}
291238
292fn mirJmpCall(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
239fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
293240 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
294241 const flag = @truncate(u1, ops.flags);
295242 if (flag == 0) {
296243 const target = emit.mir.instructions.items(.data)[inst].inst;
297 const opc: u8 = switch (tag) {
298 .jmp => 0xe9,
299 .call => 0xe8,
300 else => unreachable,
301 };
302244 const source = emit.code.items.len;
303 const encoder = try Encoder.init(emit.code, 5);
304 encoder.opcode_1byte(opc);
245 try lowerToDEnc(tag, 0, emit.code);
305246 try emit.relocs.append(emit.bin_file.allocator, .{
306247 .source = source,
307248 .target = target,
308 .offset = emit.code.items.len,
249 .offset = emit.code.items.len - 4,
309250 .length = 5,
310251 });
311 encoder.imm32(0x0);
312252 return;
313253 }
314 const modrm_ext: u3 = switch (tag) {
315 .jmp => 0x4,
316 .call => 0x2,
317 else => unreachable,
318 };
319254 if (ops.reg1 == .none) {
320255 // JMP/CALL [imm]
321256 const imm = emit.mir.instructions.items(.data)[inst].imm;
322 const encoder = try Encoder.init(emit.code, 7);
323 encoder.opcode_1byte(0xff);
324 encoder.modRm_SIBDisp0(modrm_ext);
325 encoder.sib_disp32();
326 encoder.imm32(imm);
327 return;
257 return lowerToMEnc(tag, RegisterOrMemory.mem(null, imm), emit.code);
328258 }
329259 // JMP/CALL reg
330 const encoder = try Encoder.init(emit.code, 2);
331 encoder.opcode_1byte(0xff);
332 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());
260 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
333261}
334262
335const CondType = enum {
336 /// greater than or equal
337 gte,
338
339 /// greater than
340 gt,
341
342 /// less than
343 lt,
344
345 /// less than or equal
346 lte,
347
348 /// above or equal
349 ae,
350
351 /// above
352 a,
353
354 /// below
355 b,
356
357 /// below or equal
358 be,
359
360 /// not equal
361 ne,
362
363 /// equal
364 eq,
365
366 fn fromTagAndFlags(tag: Mir.Inst.Tag, flags: u2) CondType {
367 return switch (tag) {
368 .cond_jmp_greater_less,
369 .cond_set_byte_greater_less,
370 => switch (flags) {
371 0b00 => CondType.gte,
372 0b01 => CondType.gt,
373 0b10 => CondType.lt,
374 0b11 => CondType.lte,
375 },
376 .cond_jmp_above_below,
377 .cond_set_byte_above_below,
378 => switch (flags) {
379 0b00 => CondType.ae,
380 0b01 => CondType.a,
381 0b10 => CondType.b,
382 0b11 => CondType.be,
383 },
384 .cond_jmp_eq_ne,
385 .cond_set_byte_eq_ne,
386 => switch (@truncate(u1, flags)) {
387 0b0 => CondType.ne,
388 0b1 => CondType.eq,
389 },
390 else => unreachable,
391 };
392 }
393};
394
395inline fn getCondOpCode(tag: Mir.Inst.Tag, cond: CondType) u8 {
396 switch (cond) {
397 .gte => return switch (tag) {
398 .cond_jmp_greater_less => 0x8d,
399 .cond_set_byte_greater_less => 0x9d,
400 else => unreachable,
401 },
402 .gt => return switch (tag) {
403 .cond_jmp_greater_less => 0x8f,
404 .cond_set_byte_greater_less => 0x9f,
405 else => unreachable,
406 },
407 .lt => return switch (tag) {
408 .cond_jmp_greater_less => 0x8c,
409 .cond_set_byte_greater_less => 0x9c,
410 else => unreachable,
411 },
412 .lte => return switch (tag) {
413 .cond_jmp_greater_less => 0x8e,
414 .cond_set_byte_greater_less => 0x9e,
415 else => unreachable,
416 },
417 .ae => return switch (tag) {
418 .cond_jmp_above_below => 0x83,
419 .cond_set_byte_above_below => 0x93,
420 else => unreachable,
421 },
422 .a => return switch (tag) {
423 .cond_jmp_above_below => 0x87,
424 .cond_set_byte_greater_less => 0x97,
425 else => unreachable,
426 },
427 .b => return switch (tag) {
428 .cond_jmp_above_below => 0x82,
429 .cond_set_byte_greater_less => 0x92,
430 else => unreachable,
431 },
432 .be => return switch (tag) {
433 .cond_jmp_above_below => 0x86,
434 .cond_set_byte_greater_less => 0x96,
435 else => unreachable,
263fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
264 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
265 const target = emit.mir.instructions.items(.data)[inst].inst;
266 const tag = switch (mir_tag) {
267 .cond_jmp_greater_less => switch (ops.flags) {
268 0b00 => Tag.jge,
269 0b01 => Tag.jg,
270 0b10 => Tag.jl,
271 0b11 => Tag.jle,
436272 },
437 .eq => return switch (tag) {
438 .cond_jmp_eq_ne => 0x84,
439 .cond_set_byte_eq_ne => 0x94,
440 else => unreachable,
273 .cond_jmp_above_below => switch (ops.flags) {
274 0b00 => Tag.jae,
275 0b01 => Tag.ja,
276 0b10 => Tag.jb,
277 0b11 => Tag.jbe,
441278 },
442 .ne => return switch (tag) {
443 .cond_jmp_eq_ne => 0x85,
444 .cond_set_byte_eq_ne => 0x95,
445 else => unreachable,
279 .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) {
280 0b0 => Tag.jne,
281 0b1 => Tag.je,
446282 },
447 }
448}
449
450fn mirCondJmp(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
451 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
452 const target = emit.mir.instructions.items(.data)[inst].inst;
453 const cond = CondType.fromTagAndFlags(tag, ops.flags);
454 const opc = getCondOpCode(tag, cond);
283 else => unreachable,
284 };
455285 const source = emit.code.items.len;
456 const encoder = try Encoder.init(emit.code, 6);
457 encoder.opcode_2byte(0x0f, opc);
286 try lowerToDEnc(tag, 0, emit.code);
458287 try emit.relocs.append(emit.bin_file.allocator, .{
459288 .source = source,
460289 .target = target,
461 .offset = emit.code.items.len,
290 .offset = emit.code.items.len - 4,
462291 .length = 6,
463292 });
464 encoder.imm32(0);
465293}
466294
467fn mirCondSetByte(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
295fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
468296 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
469 const cond = CondType.fromTagAndFlags(tag, ops.flags);
470 const opc = getCondOpCode(tag, cond);
471 const encoder = try Encoder.init(emit.code, 4);
472 encoder.rex(.{
473 .w = true,
474 .b = ops.reg1.isExtended(),
475 });
476 encoder.opcode_2byte(0x0f, opc);
477 encoder.modRm_direct(0x0, ops.reg1.lowId());
297 const tag = switch (mir_tag) {
298 .cond_set_byte_greater_less => switch (ops.flags) {
299 0b00 => Tag.setge,
300 0b01 => Tag.setg,
301 0b10 => Tag.setl,
302 0b11 => Tag.setle,
303 },
304 .cond_set_byte_above_below => switch (ops.flags) {
305 0b00 => Tag.setae,
306 0b01 => Tag.seta,
307 0b10 => Tag.setb,
308 0b11 => Tag.setbe,
309 },
310 .cond_set_byte_eq_ne => switch (@truncate(u1, ops.flags)) {
311 0b0 => Tag.setne,
312 0b1 => Tag.sete,
313 },
314 else => unreachable,
315 };
316 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
478317}
479318
480319fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -482,31 +321,17 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
482321 assert(tag == .@"test");
483322 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
484323 switch (ops.flags) {
485 0b00 => blk: {
324 0b00 => {
486325 if (ops.reg2 == .none) {
487326 // TEST r/m64, imm32
327 // MI
488328 const imm = emit.mir.instructions.items(.data)[inst].imm;
489329 if (ops.reg1.to64() == .rax) {
490 // TODO reduce the size of the instruction if the immediate
491 // is smaller than 32 bits
492 const encoder = try Encoder.init(emit.code, 6);
493 encoder.rex(.{
494 .w = true,
495 });
496 encoder.opcode_1byte(0xa9);
497 encoder.imm32(imm);
498 break :blk;
330 // TEST rax, imm32
331 // I
332 return lowerToIEnc(.@"test", imm, emit.code);
499333 }
500 const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7;
501 const encoder = try Encoder.init(emit.code, 7);
502 encoder.rex(.{
503 .w = true,
504 .b = ops.reg1.isExtended(),
505 });
506 encoder.opcode_1byte(opc);
507 encoder.modRm_direct(0, ops.reg1.lowId());
508 encoder.imm8(@intCast(i8, imm));
509 break :blk;
334 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
510335 }
511336 // TEST r/m64, r64
512337 return emit.fail("TODO TEST r/m64, r64", .{});
......@@ -519,26 +344,161 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
519344 const tag = emit.mir.instructions.items(.tag)[inst];
520345 assert(tag == .ret);
521346 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
522 const encoder = try Encoder.init(emit.code, 3);
523347 switch (ops.flags) {
524348 0b00 => {
525349 // RETF imm16
350 // I
526351 const imm = emit.mir.instructions.items(.data)[inst].imm;
527 encoder.opcode_1byte(0xca);
528 encoder.imm16(@intCast(i16, imm));
352 return lowerToIEnc(.ret_far, imm, emit.code);
529353 },
530 0b01 => encoder.opcode_1byte(0xcb), // RETF
354 0b01 => return lowerToZoEnc(.ret_far, emit.code),
531355 0b10 => {
532356 // RET imm16
357 // I
533358 const imm = emit.mir.instructions.items(.data)[inst].imm;
534 encoder.opcode_1byte(0xc2);
535 encoder.imm16(@intCast(i16, imm));
359 return lowerToIEnc(.ret_near, imm, emit.code);
536360 },
537 0b11 => encoder.opcode_1byte(0xc3), // RET
361 0b11 => return lowerToZoEnc(.ret_near, emit.code),
538362 }
539363}
540364
541const EncType = enum {
365const Tag = enum {
366 adc,
367 add,
368 sub,
369 xor,
370 @"and",
371 @"or",
372 sbb,
373 cmp,
374 mov,
375 lea,
376 jmp_near,
377 call_near,
378 push,
379 pop,
380 @"test",
381 brk,
382 nop,
383 imul,
384 syscall,
385 ret_near,
386 ret_far,
387 jo,
388 jno,
389 jb,
390 jbe,
391 jc,
392 jnae,
393 jnc,
394 jae,
395 je,
396 jz,
397 jne,
398 jnz,
399 jna,
400 jnb,
401 jnbe,
402 ja,
403 js,
404 jns,
405 jpe,
406 jp,
407 jpo,
408 jnp,
409 jnge,
410 jl,
411 jge,
412 jnl,
413 jle,
414 jng,
415 jg,
416 jnle,
417 seto,
418 setno,
419 setb,
420 setc,
421 setnae,
422 setnb,
423 setnc,
424 setae,
425 sete,
426 setz,
427 setne,
428 setnz,
429 setbe,
430 setna,
431 seta,
432 setnbe,
433 sets,
434 setns,
435 setp,
436 setpe,
437 setnp,
438 setop,
439 setl,
440 setnge,
441 setnl,
442 setge,
443 setle,
444 setng,
445 setnle,
446 setg,
447
448 fn isSetCC(tag: Tag) bool {
449 return switch (tag) {
450 .seto,
451 .setno,
452 .setb,
453 .setc,
454 .setnae,
455 .setnb,
456 .setnc,
457 .setae,
458 .sete,
459 .setz,
460 .setne,
461 .setnz,
462 .setbe,
463 .setna,
464 .seta,
465 .setnbe,
466 .sets,
467 .setns,
468 .setp,
469 .setpe,
470 .setnp,
471 .setop,
472 .setl,
473 .setnge,
474 .setnl,
475 .setge,
476 .setle,
477 .setng,
478 .setnle,
479 .setg,
480 => true,
481 else => false,
482 };
483 }
484};
485
486const Encoding = enum {
487 /// OP
488 zo,
489
490 /// OP rel32
491 d,
492
493 /// OP r/m64
494 m,
495
496 /// OP r64
497 o,
498
499 /// OP imm32
500 i,
501
542502 /// OP r/m64, imm32
543503 mi,
544504
......@@ -547,223 +507,853 @@ const EncType = enum {
547507
548508 /// OP r64, r/m64
549509 rm,
510
511 /// OP r64, imm64
512 oi,
513
514 /// OP al/ax/eax/rax, moffs
515 fd,
516
517 /// OP moffs, al/ax/eax/rax
518 td,
519
520 /// OP r64, r/m64, imm32
521 rmi,
550522};
551523
552const OpCode = struct {
553 opc: u8,
554 /// Only used if `EncType == .mi`.
555 modrm_ext: u3,
524const OpCode = union(enum) {
525 one_byte: u8,
526 two_byte: struct { _1: u8, _2: u8 },
527
528 fn oneByte(opc: u8) OpCode {
529 return .{ .one_byte = opc };
530 }
531
532 fn twoByte(opc1: u8, opc2: u8) OpCode {
533 return .{ .two_byte = .{ ._1 = opc1, ._2 = opc2 } };
534 }
535
536 fn encode(opc: OpCode, encoder: Encoder) void {
537 switch (opc) {
538 .one_byte => |v| encoder.opcode_1byte(v),
539 .two_byte => |v| encoder.opcode_2byte(v._1, v._2),
540 }
541 }
542
543 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {
544 assert(opc == .one_byte);
545 encoder.opcode_withReg(opc.one_byte, reg.lowId());
546 }
556547};
557548
558inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
549inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
559550 switch (enc) {
551 .zo => return switch (tag) {
552 .ret_near => OpCode.oneByte(0xc3),
553 .ret_far => OpCode.oneByte(0xcb),
554 .brk => OpCode.oneByte(0xcc),
555 .nop => OpCode.oneByte(0x90),
556 .syscall => OpCode.twoByte(0x0f, 0x05),
557 else => null,
558 },
559 .d => return switch (tag) {
560 .jmp_near => OpCode.oneByte(0xe9),
561 .call_near => OpCode.oneByte(0xe8),
562 .jo => if (is_one_byte) OpCode.oneByte(0x70) else OpCode.twoByte(0x0f, 0x80),
563 .jno => if (is_one_byte) OpCode.oneByte(0x71) else OpCode.twoByte(0x0f, 0x81),
564 .jb, .jc, .jnae => if (is_one_byte) OpCode.oneByte(0x72) else OpCode.twoByte(0x0f, 0x82),
565 .jnb, .jnc, .jae => if (is_one_byte) OpCode.oneByte(0x73) else OpCode.twoByte(0x0f, 0x83),
566 .je, .jz => if (is_one_byte) OpCode.oneByte(0x74) else OpCode.twoByte(0x0f, 0x84),
567 .jne, .jnz => if (is_one_byte) OpCode.oneByte(0x75) else OpCode.twoByte(0x0f, 0x85),
568 .jna, .jbe => if (is_one_byte) OpCode.oneByte(0x76) else OpCode.twoByte(0x0f, 0x86),
569 .jnbe, .ja => if (is_one_byte) OpCode.oneByte(0x77) else OpCode.twoByte(0x0f, 0x87),
570 .js => if (is_one_byte) OpCode.oneByte(0x78) else OpCode.twoByte(0x0f, 0x88),
571 .jns => if (is_one_byte) OpCode.oneByte(0x79) else OpCode.twoByte(0x0f, 0x89),
572 .jpe, .jp => if (is_one_byte) OpCode.oneByte(0x7a) else OpCode.twoByte(0x0f, 0x8a),
573 .jpo, .jnp => if (is_one_byte) OpCode.oneByte(0x7b) else OpCode.twoByte(0x0f, 0x8b),
574 .jnge, .jl => if (is_one_byte) OpCode.oneByte(0x7c) else OpCode.twoByte(0x0f, 0x8c),
575 .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d),
576 .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e),
577 .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f),
578 else => null,
579 },
580 .m => return switch (tag) {
581 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),
582 .pop => OpCode.oneByte(0x8f),
583 .seto => OpCode.twoByte(0x0f, 0x90),
584 .setno => OpCode.twoByte(0x0f, 0x91),
585 .setb, .setc, .setnae => OpCode.twoByte(0x0f, 0x92),
586 .setnb, .setnc, .setae => OpCode.twoByte(0x0f, 0x93),
587 .sete, .setz => OpCode.twoByte(0x0f, 0x94),
588 .setne, .setnz => OpCode.twoByte(0x0f, 0x95),
589 .setbe, .setna => OpCode.twoByte(0x0f, 0x96),
590 .seta, .setnbe => OpCode.twoByte(0x0f, 0x97),
591 .sets => OpCode.twoByte(0x0f, 0x98),
592 .setns => OpCode.twoByte(0x0f, 0x99),
593 .setp, .setpe => OpCode.twoByte(0x0f, 0x9a),
594 .setnp, .setop => OpCode.twoByte(0x0f, 0x9b),
595 .setl, .setnge => OpCode.twoByte(0x0f, 0x9c),
596 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
597 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
598 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
599 else => null,
600 },
601 .o => return switch (tag) {
602 .push => OpCode.oneByte(0x50),
603 .pop => OpCode.oneByte(0x58),
604 else => null,
605 },
606 .i => return switch (tag) {
607 .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68),
608 .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9),
609 .ret_near => OpCode.oneByte(0xc2),
610 .ret_far => OpCode.oneByte(0xca),
611 else => null,
612 },
560613 .mi => return switch (tag) {
561 .adc => .{ .opc = 0x81, .modrm_ext = 0x2 },
562 .add => .{ .opc = 0x81, .modrm_ext = 0x0 },
563 .sub => .{ .opc = 0x81, .modrm_ext = 0x5 },
564 .xor => .{ .opc = 0x81, .modrm_ext = 0x6 },
565 .@"and" => .{ .opc = 0x81, .modrm_ext = 0x4 },
566 .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },
567 .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },
568 .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },
569 .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 },
570 else => unreachable,
614 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
615 .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
616 .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
617 else => null,
571618 },
572 .mr => {
573 const opc: u8 = switch (tag) {
574 .adc => 0x11,
575 .add => 0x01,
576 .sub => 0x29,
577 .xor => 0x31,
578 .@"and" => 0x21,
579 .@"or" => 0x09,
580 .sbb => 0x19,
581 .cmp => 0x39,
582 .mov => 0x89,
583 else => unreachable,
584 };
585 return .{ .opc = opc, .modrm_ext = undefined };
619 .mr => return switch (tag) {
620 .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
621 .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),
622 .sub => OpCode.oneByte(if (is_one_byte) 0x28 else 0x29),
623 .xor => OpCode.oneByte(if (is_one_byte) 0x30 else 0x31),
624 .@"and" => OpCode.oneByte(if (is_one_byte) 0x20 else 0x21),
625 .@"or" => OpCode.oneByte(if (is_one_byte) 0x08 else 0x09),
626 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),
627 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
628 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
629 else => null,
586630 },
587 .rm => {
588 const opc: u8 = switch (tag) {
589 .adc => 0x13,
590 .add => 0x03,
591 .sub => 0x2b,
592 .xor => 0x33,
593 .@"and" => 0x23,
594 .@"or" => 0x0b,
595 .sbb => 0x1b,
596 .cmp => 0x3b,
597 .mov => 0x8b,
598 else => unreachable,
599 };
600 return .{ .opc = opc, .modrm_ext = undefined };
631 .rm => return switch (tag) {
632 .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13),
633 .add => OpCode.oneByte(if (is_one_byte) 0x02 else 0x03),
634 .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b),
635 .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33),
636 .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23),
637 .@"or" => OpCode.oneByte(if (is_one_byte) 0x0b else 0x0b),
638 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),
639 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),
640 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),
641 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
642 .imul => OpCode.twoByte(0x0f, 0xaf),
643 else => null,
644 },
645 .oi => return switch (tag) {
646 .mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8),
647 else => null,
648 },
649 .fd => return switch (tag) {
650 .mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1),
651 else => null,
652 },
653 .td => return switch (tag) {
654 .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
655 else => null,
656 },
657 .rmi => return switch (tag) {
658 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
659 else => null,
601660 },
602661 }
603662}
604663
605fn mirArith(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
606 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
607 switch (ops.flags) {
608 0b00 => blk: {
609 if (ops.reg2 == .none) {
610 // OP reg1, imm32
611 // OP r/m64, imm32
612 const imm = emit.mir.instructions.items(.data)[inst].imm;
613 const opcode = getArithOpCode(tag, .mi);
614 const encoder = try Encoder.init(emit.code, 7);
664inline fn getModRmExt(tag: Tag) ?u3 {
665 return switch (tag) {
666 .adc => 0x2,
667 .add => 0x0,
668 .sub => 0x5,
669 .xor => 0x6,
670 .@"and" => 0x4,
671 .@"or" => 0x1,
672 .sbb => 0x3,
673 .cmp => 0x7,
674 .mov => 0x0,
675 .jmp_near => 0x4,
676 .call_near => 0x2,
677 .push => 0x6,
678 .pop => 0x0,
679 .@"test" => 0x0,
680 .seto,
681 .setno,
682 .setb,
683 .setc,
684 .setnae,
685 .setnb,
686 .setnc,
687 .setae,
688 .sete,
689 .setz,
690 .setne,
691 .setnz,
692 .setbe,
693 .setna,
694 .seta,
695 .setnbe,
696 .sets,
697 .setns,
698 .setp,
699 .setpe,
700 .setnp,
701 .setop,
702 .setl,
703 .setnge,
704 .setnl,
705 .setge,
706 .setle,
707 .setng,
708 .setnle,
709 .setg,
710 => 0x0,
711 else => null,
712 };
713}
714
715const ScaleIndexBase = struct {
716 scale: u2,
717 index_reg: ?Register,
718 base_reg: ?Register,
719};
720
721const Memory = struct {
722 reg: ?Register,
723 rip: bool = false,
724 disp: i32,
725 sib: ?ScaleIndexBase = null,
726};
727
728const RegisterOrMemory = union(enum) {
729 register: Register,
730 memory: Memory,
731
732 fn reg(register: Register) RegisterOrMemory {
733 return .{ .register = register };
734 }
735
736 fn mem(register: ?Register, disp: i32) RegisterOrMemory {
737 return .{
738 .memory = .{
739 .reg = register,
740 .disp = disp,
741 },
742 };
743 }
744
745 fn rip(disp: i32) RegisterOrMemory {
746 return .{
747 .memory = .{
748 .reg = null,
749 .rip = true,
750 .disp = disp,
751 },
752 };
753 }
754};
755
756fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
757 const opc = getOpCode(tag, .zo, false).?;
758 const encoder = try Encoder.init(code, 1);
759 opc.encode(encoder);
760}
761
762fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
763 if (tag == .ret_far or tag == .ret_near) {
764 const encoder = try Encoder.init(code, 3);
765 const opc = getOpCode(tag, .i, false).?;
766 opc.encode(encoder);
767 encoder.imm16(@intCast(i16, imm));
768 return;
769 }
770 const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?;
771 const encoder = try Encoder.init(code, 5);
772 if (immOpSize(imm) == 16) {
773 encoder.opcode_1byte(0x66);
774 }
775 opc.encode(encoder);
776 if (immOpSize(imm) == 8) {
777 encoder.imm8(@intCast(i8, imm));
778 } else if (immOpSize(imm) == 16) {
779 encoder.imm16(@intCast(i16, imm));
780 } else {
781 encoder.imm32(imm);
782 }
783}
784
785fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
786 if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal?
787 const opc = getOpCode(tag, .o, false).?;
788 const encoder = try Encoder.init(code, 3);
789 if (reg.size() == 16) {
790 encoder.opcode_1byte(0x66);
791 }
792 encoder.rex(.{
793 .w = false,
794 .b = reg.isExtended(),
795 });
796 opc.encodeWithReg(encoder, reg);
797}
798
799fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
800 const opc = getOpCode(tag, .d, false).?;
801 const encoder = try Encoder.init(code, 6);
802 opc.encode(encoder);
803 encoder.imm32(imm);
804}
805
806fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
807 const opc = getOpCode(tag, .m, false).?;
808 const modrm_ext = getModRmExt(tag).?;
809 switch (reg_or_mem) {
810 .register => |reg| {
811 // TODO clean this up!
812 if (reg.size() != 64) {
813 if (reg.size() != 8 and !tag.isSetCC()) return error.EmitFail;
814 }
815 const encoder = try Encoder.init(code, 3);
816 encoder.rex(.{
817 .w = tag.isSetCC(),
818 .b = reg.isExtended(),
819 });
820 opc.encode(encoder);
821 encoder.modRm_direct(modrm_ext, reg.lowId());
822 },
823 .memory => |mem_op| {
824 const encoder = try Encoder.init(code, 8);
825 if (mem_op.reg) |reg| {
826 // TODO clean this up!
827 if (reg.size() != 64) {
828 if (reg.size() != 8 and !tag.isSetCC()) return error.EmitFail;
829 }
615830 encoder.rex(.{
616 .w = ops.reg1.size() == 64,
617 .b = ops.reg1.isExtended(),
831 .w = tag.isSetCC(),
832 .b = reg.isExtended(),
618833 });
619 if (tag != .mov and imm <= math.maxInt(i8)) {
620 encoder.opcode_1byte(opcode.opc + 2);
621 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
622 encoder.imm8(@intCast(i8, imm));
834 opc.encode(encoder);
835 if (reg.lowId() == 4) {
836 if (mem_op.disp == 0) {
837 encoder.modRm_SIBDisp0(modrm_ext);
838 encoder.sib_base(reg.lowId());
839 } else if (immOpSize(mem_op.disp) == 8) {
840 encoder.modRm_SIBDisp8(modrm_ext);
841 encoder.sib_baseDisp8(reg.lowId());
842 encoder.disp8(@intCast(i8, mem_op.disp));
843 } else {
844 encoder.modRm_SIBDisp32(modrm_ext);
845 encoder.sib_baseDisp32(reg.lowId());
846 encoder.disp32(mem_op.disp);
847 }
623848 } else {
624 encoder.opcode_1byte(opcode.opc);
625 encoder.modRm_direct(opcode.modrm_ext, ops.reg1.lowId());
626 encoder.imm32(imm);
849 if (mem_op.disp == 0) {
850 encoder.modRm_indirectDisp0(modrm_ext, reg.lowId());
851 } else if (immOpSize(mem_op.disp) == 8) {
852 encoder.modRm_indirectDisp8(modrm_ext, reg.lowId());
853 encoder.disp8(@intCast(i8, mem_op.disp));
854 } else {
855 encoder.modRm_indirectDisp32(modrm_ext, reg.lowId());
856 encoder.disp32(mem_op.disp);
857 }
627858 }
628 break :blk;
859 } else {
860 opc.encode(encoder);
861 if (mem_op.rip) {
862 encoder.modRm_RIPDisp32(modrm_ext);
863 } else {
864 encoder.modRm_SIBDisp0(modrm_ext);
865 encoder.sib_disp32();
866 }
867 encoder.disp32(mem_op.disp);
868 }
869 },
870 }
871}
872
873fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
874 return lowerToTdFdEnc(tag, reg, moffs, code, true);
875}
876
877fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) InnerError!void {
878 return lowerToTdFdEnc(tag, reg, moffs, code, false);
879}
880
881fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) InnerError!void {
882 if (reg.lowId() != Register.rax.lowId()) return error.EmitFail;
883 if (reg.size() != immOpSize(moffs)) return error.EmitFail;
884 const opc = if (td)
885 getOpCode(tag, .td, reg.size() == 8).?
886 else
887 getOpCode(tag, .fd, reg.size() == 8).?;
888 const encoder = try Encoder.init(code, 10);
889 if (reg.size() == 16) {
890 encoder.opcode_1byte(0x66);
891 }
892 encoder.rex(.{
893 .w = reg.size() == 64,
894 });
895 opc.encode(encoder);
896 switch (reg.size()) {
897 8 => {
898 const moffs8 = try math.cast(i8, moffs);
899 encoder.imm8(moffs8);
900 },
901 16 => {
902 const moffs16 = try math.cast(i16, moffs);
903 encoder.imm16(moffs16);
904 },
905 32 => {
906 const moffs32 = try math.cast(i32, moffs);
907 encoder.imm32(moffs32);
908 },
909 64 => {
910 encoder.imm64(@bitCast(u64, moffs));
911 },
912 else => unreachable,
913 }
914}
915
916fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) InnerError!void {
917 if (reg.size() != immOpSize(imm)) return error.EmitFail;
918 const opc = getOpCode(tag, .oi, reg.size() == 8).?;
919 const encoder = try Encoder.init(code, 10);
920 if (reg.size() == 16) {
921 encoder.opcode_1byte(0x66);
922 }
923 encoder.rex(.{
924 .w = reg.size() == 64,
925 .b = reg.isExtended(),
926 });
927 opc.encodeWithReg(encoder, reg);
928 switch (reg.size()) {
929 8 => {
930 const imm8 = try math.cast(i8, imm);
931 encoder.imm8(imm8);
932 },
933 16 => {
934 const imm16 = try math.cast(i16, imm);
935 encoder.imm16(imm16);
936 },
937 32 => {
938 const imm32 = try math.cast(i32, imm);
939 encoder.imm32(imm32);
940 },
941 64 => {
942 encoder.imm64(@bitCast(u64, imm));
943 },
944 else => unreachable,
945 }
946}
947
948fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
949 const modrm_ext = getModRmExt(tag).?;
950 switch (reg_or_mem) {
951 .register => |dst_reg| {
952 const opc = getOpCode(tag, .mi, dst_reg.size() == 8).?;
953 const encoder = try Encoder.init(code, 7);
954 if (dst_reg.size() == 16) {
955 // 0x66 prefix switches to the non-default size; here we assume a switch from
956 // the default 32bits to 16bits operand-size.
957 // More info: https://www.cs.uni-potsdam.de/desn/lehre/ss15/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf#page=32&zoom=auto,-159,773
958 encoder.opcode_1byte(0x66);
629959 }
630 // OP reg1, reg2
631 // OP r/m64, r64
632 const opcode = getArithOpCode(tag, .mr);
633 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
634 const encoder = try Encoder.init(emit.code, 3);
635960 encoder.rex(.{
636 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
637 .r = ops.reg1.isExtended(),
638 .b = ops.reg2.isExtended(),
961 .w = dst_reg.size() == 64,
962 .b = dst_reg.isExtended(),
639963 });
640 encoder.opcode_1byte(opc);
641 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
964 opc.encode(encoder);
965 encoder.modRm_direct(modrm_ext, dst_reg.lowId());
966 switch (dst_reg.size()) {
967 8 => {
968 const imm8 = try math.cast(i8, imm);
969 encoder.imm8(imm8);
970 },
971 16 => {
972 const imm16 = try math.cast(i16, imm);
973 encoder.imm16(imm16);
974 },
975 32, 64 => encoder.imm32(imm),
976 else => unreachable,
977 }
642978 },
643 0b01 => blk: {
644 const imm = emit.mir.instructions.items(.data)[inst].imm;
645 const opcode = getArithOpCode(tag, .rm);
646 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
647 if (ops.reg2 == .none) {
648 // OP reg1, [imm32]
649 // OP r64, r/m64
650 const encoder = try Encoder.init(emit.code, 8);
979 .memory => |dst_mem| {
980 const opc = getOpCode(tag, .mi, false).?;
981 const encoder = try Encoder.init(code, 12);
982 if (dst_mem.reg) |dst_reg| {
983 // Register dst_reg can either be 64bit or 32bit in size.
984 // TODO for memory operand, immediate operand pair, we currently
985 // have no way of flagging whether the immediate can be 8-, 16- or
986 // 32-bit and whether the corresponding memory operand is respectively
987 // a byte, word or dword ptr.
988 // TODO we currently don't have a way to flag imm32 64bit sign extended
989 if (dst_reg.size() != 64) return error.EmitFail;
651990 encoder.rex(.{
652 .w = ops.reg1.size() == 64,
653 .b = ops.reg1.isExtended(),
991 .w = false,
992 .b = dst_reg.isExtended(),
654993 });
655 encoder.opcode_1byte(opc);
656 encoder.modRm_SIBDisp0(ops.reg1.lowId());
657 encoder.sib_disp32();
658 encoder.disp32(imm);
659 break :blk;
994 opc.encode(encoder);
995 if (dst_reg.lowId() == 4) {
996 if (dst_mem.disp == 0) {
997 encoder.modRm_SIBDisp0(modrm_ext);
998 encoder.sib_base(dst_reg.lowId());
999 } else if (immOpSize(dst_mem.disp) == 8) {
1000 encoder.modRm_SIBDisp8(modrm_ext);
1001 encoder.sib_baseDisp8(dst_reg.lowId());
1002 encoder.disp8(@intCast(i8, dst_mem.disp));
1003 } else {
1004 encoder.modRm_SIBDisp32(modrm_ext);
1005 encoder.sib_baseDisp32(dst_reg.lowId());
1006 encoder.disp32(dst_mem.disp);
1007 }
1008 } else {
1009 if (dst_mem.disp == 0) {
1010 encoder.modRm_indirectDisp0(modrm_ext, dst_reg.lowId());
1011 } else if (immOpSize(dst_mem.disp) == 8) {
1012 encoder.modRm_indirectDisp8(modrm_ext, dst_reg.lowId());
1013 encoder.disp8(@intCast(i8, dst_mem.disp));
1014 } else {
1015 encoder.modRm_indirectDisp32(modrm_ext, dst_reg.lowId());
1016 encoder.disp32(dst_mem.disp);
1017 }
1018 }
1019 } else {
1020 opc.encode(encoder);
1021 if (dst_mem.rip) {
1022 encoder.modRm_RIPDisp32(modrm_ext);
1023 } else {
1024 encoder.modRm_SIBDisp0(modrm_ext);
1025 encoder.sib_disp32();
1026 }
1027 encoder.disp32(dst_mem.disp);
6601028 }
661 // OP reg1, [reg2 + imm32]
662 // OP r64, r/m64
663 const encoder = try Encoder.init(emit.code, 7);
1029 encoder.imm32(imm);
1030 },
1031 }
1032}
1033
1034fn lowerToRmEnc(
1035 tag: Tag,
1036 reg: Register,
1037 reg_or_mem: RegisterOrMemory,
1038 code: *std.ArrayList(u8),
1039) InnerError!void {
1040 const opc = getOpCode(tag, .rm, reg.size() == 8).?;
1041 switch (reg_or_mem) {
1042 .register => |src_reg| {
1043 if (reg.size() != src_reg.size()) return error.EmitFail;
1044 const encoder = try Encoder.init(code, 3);
6641045 encoder.rex(.{
665 .w = ops.reg1.size() == 64,
666 .r = ops.reg1.isExtended(),
667 .b = ops.reg2.isExtended(),
1046 .w = reg.size() == 64,
1047 .r = reg.isExtended(),
1048 .b = src_reg.isExtended(),
6681049 });
669 encoder.opcode_1byte(opc);
670 if (imm <= math.maxInt(i8)) {
671 encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId());
672 encoder.disp8(@intCast(i8, imm));
1050 opc.encode(encoder);
1051 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
1052 },
1053 .memory => |src_mem| {
1054 const encoder = try Encoder.init(code, 9);
1055 if (reg.size() == 16) {
1056 encoder.opcode_1byte(0x66);
1057 }
1058 if (src_mem.reg) |src_reg| {
1059 // TODO handle 32-bit base register - requires prefix 0x67
1060 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1061 if (src_reg.size() != 64) return error.EmitFail;
1062 encoder.rex(.{
1063 .w = reg.size() == 64,
1064 .r = reg.isExtended(),
1065 .b = src_reg.isExtended(),
1066 });
1067 opc.encode(encoder);
1068 if (src_reg.lowId() == 4) {
1069 if (src_mem.disp == 0) {
1070 encoder.modRm_SIBDisp0(reg.lowId());
1071 encoder.sib_base(src_reg.lowId());
1072 } else if (immOpSize(src_mem.disp) == 8) {
1073 encoder.modRm_SIBDisp8(reg.lowId());
1074 encoder.sib_baseDisp8(src_reg.lowId());
1075 encoder.disp8(@intCast(i8, src_mem.disp));
1076 } else {
1077 encoder.modRm_SIBDisp32(reg.lowId());
1078 encoder.sib_baseDisp32(src_reg.lowId());
1079 encoder.disp32(src_mem.disp);
1080 }
1081 } else {
1082 if (src_mem.disp == 0) {
1083 encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId());
1084 } else if (immOpSize(src_mem.disp) == 8) {
1085 encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId());
1086 encoder.disp8(@intCast(i8, src_mem.disp));
1087 } else {
1088 encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId());
1089 encoder.disp32(src_mem.disp);
1090 }
1091 }
6731092 } else {
674 encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId());
675 encoder.disp32(imm);
1093 encoder.rex(.{
1094 .w = reg.size() == 64,
1095 .r = reg.isExtended(),
1096 });
1097 opc.encode(encoder);
1098 if (src_mem.rip) {
1099 encoder.modRm_RIPDisp32(reg.lowId());
1100 } else {
1101 encoder.modRm_SIBDisp0(reg.lowId());
1102 encoder.sib_disp32();
1103 }
1104 encoder.disp32(src_mem.disp);
6761105 }
6771106 },
678 0b10 => blk: {
679 if (ops.reg2 == .none) {
680 // OP [reg1 + 0], imm32
681 // OP r/m64, imm32
682 const imm = emit.mir.instructions.items(.data)[inst].imm;
683 const opcode = getArithOpCode(tag, .mi);
684 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
685 const encoder = try Encoder.init(emit.code, 7);
1107 }
1108}
1109
1110fn lowerToMrEnc(
1111 tag: Tag,
1112 reg_or_mem: RegisterOrMemory,
1113 reg: Register,
1114 code: *std.ArrayList(u8),
1115) InnerError!void {
1116 // We use size of source register reg to work out which
1117 // variant of memory ptr to pick:
1118 // * reg is 64bit - qword ptr
1119 // * reg is 32bit - dword ptr
1120 // * reg is 16bit - word ptr
1121 // * reg is 8bit - byte ptr
1122 const opc = getOpCode(tag, .mr, reg.size() == 8).?;
1123 switch (reg_or_mem) {
1124 .register => |dst_reg| {
1125 if (dst_reg.size() != reg.size()) return error.EmitFail;
1126 const encoder = try Encoder.init(code, 3);
1127 encoder.rex(.{
1128 .w = dst_reg.size() == 64,
1129 .r = reg.isExtended(),
1130 .b = dst_reg.isExtended(),
1131 });
1132 opc.encode(encoder);
1133 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
1134 },
1135 .memory => |dst_mem| {
1136 const encoder = try Encoder.init(code, 9);
1137 if (reg.size() == 16) {
1138 encoder.opcode_1byte(0x66);
1139 }
1140 if (dst_mem.reg) |dst_reg| {
1141 if (dst_reg.size() != 64) return error.EmitFail;
1142 encoder.rex(.{
1143 .w = reg.size() == 64,
1144 .r = reg.isExtended(),
1145 .b = dst_reg.isExtended(),
1146 });
1147 opc.encode(encoder);
1148 if (dst_reg.lowId() == 4) {
1149 if (dst_mem.disp == 0) {
1150 encoder.modRm_SIBDisp0(reg.lowId());
1151 encoder.sib_base(dst_reg.lowId());
1152 } else if (immOpSize(dst_mem.disp) == 8) {
1153 encoder.modRm_SIBDisp8(reg.lowId());
1154 encoder.sib_baseDisp8(dst_reg.lowId());
1155 encoder.disp8(@intCast(i8, dst_mem.disp));
1156 } else {
1157 encoder.modRm_SIBDisp32(reg.lowId());
1158 encoder.sib_baseDisp32(dst_reg.lowId());
1159 encoder.disp32(dst_mem.disp);
1160 }
1161 } else {
1162 if (dst_mem.disp == 0) {
1163 encoder.modRm_indirectDisp0(reg.lowId(), dst_reg.lowId());
1164 } else if (immOpSize(dst_mem.disp) == 8) {
1165 encoder.modRm_indirectDisp8(reg.lowId(), dst_reg.lowId());
1166 encoder.disp8(@intCast(i8, dst_mem.disp));
1167 } else {
1168 encoder.modRm_indirectDisp32(reg.lowId(), dst_reg.lowId());
1169 encoder.disp32(dst_mem.disp);
1170 }
1171 }
1172 } else {
6861173 encoder.rex(.{
687 .w = ops.reg1.size() == 64,
688 .b = ops.reg1.isExtended(),
1174 .w = reg.size() == 64,
1175 .r = reg.isExtended(),
6891176 });
690 encoder.opcode_1byte(opc);
691 encoder.modRm_indirectDisp0(opcode.modrm_ext, ops.reg1.lowId());
692 if (imm <= math.maxInt(i8)) {
693 encoder.imm8(@intCast(i8, imm));
694 } else if (imm <= math.maxInt(i16)) {
695 encoder.imm16(@intCast(i16, imm));
1177 opc.encode(encoder);
1178 if (dst_mem.rip) {
1179 encoder.modRm_RIPDisp32(reg.lowId());
6961180 } else {
697 encoder.imm32(imm);
1181 encoder.modRm_SIBDisp0(reg.lowId());
1182 encoder.sib_disp32();
6981183 }
699 break :blk;
1184 encoder.disp32(dst_mem.disp);
7001185 }
701 // OP [reg1 + imm32], reg2
702 // OP r/m64, r64
703 const imm = emit.mir.instructions.items(.data)[inst].imm;
704 const opcode = getArithOpCode(tag, .mr);
705 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
706 const encoder = try Encoder.init(emit.code, 7);
1186 },
1187 }
1188}
1189
1190fn lowerToRmiEnc(
1191 tag: Tag,
1192 reg: Register,
1193 reg_or_mem: RegisterOrMemory,
1194 imm: i32,
1195 code: *std.ArrayList(u8),
1196) InnerError!void {
1197 const opc = getOpCode(tag, .rmi, reg.size() == 8).?;
1198 switch (reg_or_mem) {
1199 .register => |src_reg| {
1200 if (reg.size() != src_reg.size()) return error.EmitFail;
1201 const encoder = try Encoder.init(code, 7);
7071202 encoder.rex(.{
708 .w = ops.reg2.size() == 64,
709 .r = ops.reg1.isExtended(),
710 .b = ops.reg2.isExtended(),
1203 .w = reg.size() == 64,
1204 .r = reg.isExtended(),
1205 .b = src_reg.isExtended(),
7111206 });
712 encoder.opcode_1byte(opc);
713 if (imm <= math.maxInt(i8)) {
714 encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId());
715 encoder.disp8(@intCast(i8, imm));
716 } else {
717 encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId());
718 encoder.disp32(imm);
1207 opc.encode(encoder);
1208 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
1209 switch (reg.size()) {
1210 8 => {
1211 const imm8 = try math.cast(i8, imm);
1212 encoder.imm8(imm8);
1213 },
1214 16 => {
1215 const imm16 = try math.cast(i16, imm);
1216 encoder.imm16(imm16);
1217 },
1218 32, 64 => encoder.imm32(imm),
1219 else => unreachable,
7191220 }
7201221 },
721 0b11 => blk: {
722 if (ops.reg2 == .none) {
723 // OP [reg1 + imm32], imm32
724 // OP r/m64, imm32
725 const payload = emit.mir.instructions.items(.data)[inst].payload;
726 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
727 const opcode = getArithOpCode(tag, .mi);
728 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
729 const encoder = try Encoder.init(emit.code, 11);
1222 .memory => |src_mem| {
1223 const encoder = try Encoder.init(code, 13);
1224 if (reg.size() == 16) {
1225 encoder.opcode_1byte(0x66);
1226 }
1227 if (src_mem.reg) |src_reg| {
1228 // TODO handle 32-bit base register - requires prefix 0x67
1229 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1230 if (src_reg.size() != 64) return error.EmitFail;
7301231 encoder.rex(.{
731 .w = false,
732 .b = ops.reg1.isExtended(),
1232 .w = reg.size() == 64,
1233 .r = reg.isExtended(),
1234 .b = src_reg.isExtended(),
7331235 });
734 encoder.opcode_1byte(opc);
735 if (imm_pair.dest_off <= math.maxInt(i8)) {
736 encoder.modRm_indirectDisp8(opcode.modrm_ext, ops.reg1.lowId());
737 encoder.disp8(@intCast(i8, imm_pair.dest_off));
1236 opc.encode(encoder);
1237 if (src_reg.lowId() == 4) {
1238 if (src_mem.disp == 0) {
1239 encoder.modRm_SIBDisp0(reg.lowId());
1240 encoder.sib_base(src_reg.lowId());
1241 } else if (immOpSize(src_mem.disp) == 8) {
1242 encoder.modRm_SIBDisp8(reg.lowId());
1243 encoder.sib_baseDisp8(src_reg.lowId());
1244 encoder.disp8(@intCast(i8, src_mem.disp));
1245 } else {
1246 encoder.modRm_SIBDisp32(reg.lowId());
1247 encoder.sib_baseDisp32(src_reg.lowId());
1248 encoder.disp32(src_mem.disp);
1249 }
7381250 } else {
739 encoder.modRm_indirectDisp32(opcode.modrm_ext, ops.reg1.lowId());
740 encoder.disp32(imm_pair.dest_off);
1251 if (src_mem.disp == 0) {
1252 encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId());
1253 } else if (immOpSize(src_mem.disp) == 8) {
1254 encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId());
1255 encoder.disp8(@intCast(i8, src_mem.disp));
1256 } else {
1257 encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId());
1258 encoder.disp32(src_mem.disp);
1259 }
7411260 }
742 encoder.imm32(imm_pair.operand);
743 break :blk;
1261 } else {
1262 encoder.rex(.{
1263 .w = reg.size() == 64,
1264 .r = reg.isExtended(),
1265 });
1266 opc.encode(encoder);
1267 if (src_mem.rip) {
1268 encoder.modRm_RIPDisp32(reg.lowId());
1269 } else {
1270 encoder.modRm_SIBDisp0(reg.lowId());
1271 encoder.sib_disp32();
1272 }
1273 encoder.disp32(src_mem.disp);
7441274 }
745 // TODO clearly mov doesn't belong here; for other, arithemtic ops,
746 // this is the same as 0b00.
747 const opcode = getArithOpCode(tag, if (tag == .mov) .rm else .mr);
748 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
749 const encoder = try Encoder.init(emit.code, 3);
750 encoder.rex(.{
751 .w = ops.reg1.size() == 64 and ops.reg2.size() == 64,
752 .r = ops.reg1.isExtended(),
753 .b = ops.reg2.isExtended(),
754 });
755 encoder.opcode_1byte(opc);
756 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
1275 encoder.imm32(imm);
7571276 },
7581277 }
7591278}
7601279
761fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
1280fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1281 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
1282 switch (ops.flags) {
1283 0b00 => {
1284 if (ops.reg2 == .none) {
1285 // mov reg1, imm32
1286 // MI
1287 const imm = emit.mir.instructions.items(.data)[inst].imm;
1288 return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
1289 }
1290 // mov reg1, reg2
1291 // RM
1292 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1293 },
1294 0b01 => {
1295 const imm = emit.mir.instructions.items(.data)[inst].imm;
1296 if (ops.reg2 == .none) {
1297 // mov reg1, [imm32]
1298 // RM
1299 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(null, imm), emit.code);
1300 }
1301 // mov reg1, [reg2 + imm32]
1302 // RM
1303 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ops.reg2, imm), emit.code);
1304 },
1305 0b10 => {
1306 if (ops.reg2 == .none) {
1307 // mov dword ptr [reg1 + 0], imm32
1308 // MI
1309 const imm = emit.mir.instructions.items(.data)[inst].imm;
1310 return lowerToMiEnc(tag, RegisterOrMemory.mem(ops.reg1, 0), imm, emit.code);
1311 }
1312 // mov [reg1 + imm32], reg2
1313 // MR
1314 const imm = emit.mir.instructions.items(.data)[inst].imm;
1315 return lowerToMrEnc(tag, RegisterOrMemory.mem(ops.reg1, imm), ops.reg2, emit.code);
1316 },
1317 0b11 => {
1318 if (ops.reg2 == .none) {
1319 // mov dword ptr [reg1 + imm32], imm32
1320 // MI
1321 const payload = emit.mir.instructions.items(.data)[inst].payload;
1322 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
1323 return lowerToMiEnc(
1324 tag,
1325 RegisterOrMemory.mem(ops.reg1, imm_pair.dest_off),
1326 imm_pair.operand,
1327 emit.code,
1328 );
1329 }
1330 return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{});
1331 },
1332 }
1333}
1334
1335fn immOpSize(imm: i64) u8 {
1336 blk: {
1337 _ = math.cast(i8, imm) catch break :blk;
1338 return 8;
1339 }
1340 blk: {
1341 _ = math.cast(i16, imm) catch break :blk;
1342 return 16;
1343 }
1344 blk: {
1345 _ = math.cast(i32, imm) catch break :blk;
1346 return 32;
1347 }
1348 return 64;
1349}
1350
1351// TODO
1352fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
7621353 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
7631354 const scale = ops.flags;
7641355 // OP reg1, [reg2 + scale*rcx + imm32]
765 const opcode = getArithOpCode(tag, .rm);
766 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1356 const opc = getOpCode(tag, .rm, ops.reg1.size() == 8).?;
7671357 const imm = emit.mir.instructions.items(.data)[inst].imm;
7681358 const encoder = try Encoder.init(emit.code, 8);
7691359 encoder.rex(.{
......@@ -771,7 +1361,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
7711361 .r = ops.reg1.isExtended(),
7721362 .b = ops.reg2.isExtended(),
7731363 });
774 encoder.opcode_1byte(opc);
1364 opc.encode(encoder);
7751365 if (imm <= math.maxInt(i8)) {
7761366 encoder.modRm_SIBDisp8(ops.reg1.lowId());
7771367 encoder.sib_scaleIndexBaseDisp8(scale, Register.rcx.lowId(), ops.reg2.lowId());
......@@ -783,22 +1373,23 @@ fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
7831373 }
7841374}
7851375
786fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
1376// TODO
1377fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
7871378 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
7881379 const scale = ops.flags;
7891380 const imm = emit.mir.instructions.items(.data)[inst].imm;
7901381
7911382 if (ops.reg2 == .none) {
7921383 // OP [reg1 + scale*rax + 0], imm32
793 const opcode = getArithOpCode(tag, .mi);
794 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1384 const opc = getOpCode(tag, .mi, ops.reg1.size() == 8).?;
1385 const modrm_ext = getModRmExt(tag).?;
7951386 const encoder = try Encoder.init(emit.code, 8);
7961387 encoder.rex(.{
7971388 .w = ops.reg1.size() == 64,
7981389 .b = ops.reg1.isExtended(),
7991390 });
800 encoder.opcode_1byte(opc);
801 encoder.modRm_SIBDisp0(opcode.modrm_ext);
1391 opc.encode(encoder);
1392 encoder.modRm_SIBDisp0(modrm_ext);
8021393 encoder.sib_scaleIndexBase(scale, Register.rax.lowId(), ops.reg1.lowId());
8031394 if (imm <= math.maxInt(i8)) {
8041395 encoder.imm8(@intCast(i8, imm));
......@@ -811,15 +1402,14 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
8111402 }
8121403
8131404 // OP [reg1 + scale*rax + imm32], reg2
814 const opcode = getArithOpCode(tag, .mr);
815 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1405 const opc = getOpCode(tag, .mr, ops.reg1.size() == 8).?;
8161406 const encoder = try Encoder.init(emit.code, 8);
8171407 encoder.rex(.{
8181408 .w = ops.reg1.size() == 64,
8191409 .r = ops.reg2.isExtended(),
8201410 .b = ops.reg1.isExtended(),
8211411 });
822 encoder.opcode_1byte(opc);
1412 opc.encode(encoder);
8231413 if (imm <= math.maxInt(i8)) {
8241414 encoder.modRm_SIBDisp8(ops.reg2.lowId());
8251415 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
......@@ -831,25 +1421,26 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
8311421 }
8321422}
8331423
834fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
1424// TODO
1425fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
8351426 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
8361427 const scale = ops.flags;
8371428 const payload = emit.mir.instructions.items(.data)[inst].payload;
8381429 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
839 const opcode = getArithOpCode(tag, .mi);
840 const opc = if (ops.reg1.size() == 8) opcode.opc - 1 else opcode.opc;
1430 const opc = getOpCode(tag, .mi, ops.reg1.size() == 8).?;
1431 const modrm_ext = getModRmExt(tag).?;
8411432 const encoder = try Encoder.init(emit.code, 2);
8421433 encoder.rex(.{
8431434 .w = ops.reg1.size() == 64,
8441435 .b = ops.reg1.isExtended(),
8451436 });
846 encoder.opcode_1byte(opc);
1437 opc.encode(encoder);
8471438 if (imm_pair.dest_off <= math.maxInt(i8)) {
848 encoder.modRm_SIBDisp8(opcode.modrm_ext);
1439 encoder.modRm_SIBDisp8(modrm_ext);
8491440 encoder.sib_scaleIndexBaseDisp8(scale, Register.rax.lowId(), ops.reg1.lowId());
8501441 encoder.disp8(@intCast(i8, imm_pair.dest_off));
8511442 } else {
852 encoder.modRm_SIBDisp32(opcode.modrm_ext);
1443 encoder.modRm_SIBDisp32(modrm_ext);
8531444 encoder.sib_scaleIndexBaseDisp32(scale, Register.rax.lowId(), ops.reg1.lowId());
8541445 encoder.disp32(imm_pair.dest_off);
8551446 }
......@@ -860,54 +1451,24 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
8601451 const tag = emit.mir.instructions.items(.tag)[inst];
8611452 assert(tag == .movabs);
8621453 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
863
864 const encoder = try Encoder.init(emit.code, 10);
865 const is_64 = blk: {
866 if (ops.flags == 0b00) {
867 // movabs reg, imm64
868 const opc: u8 = if (ops.reg1.size() == 8) 0xb0 else 0xb8;
869 if (ops.reg1.size() == 64) {
870 encoder.rex(.{
871 .w = true,
872 .b = ops.reg1.isExtended(),
873 });
874 encoder.opcode_withReg(opc, ops.reg1.lowId());
875 break :blk true;
876 }
877 break :blk false;
878 }
879 if (ops.reg1 == .none) {
880 // movabs moffs64, rax
881 const opc: u8 = if (ops.reg2.size() == 8) 0xa2 else 0xa3;
882 encoder.rex(.{
883 .w = ops.reg2.size() == 64,
884 });
885 encoder.opcode_1byte(opc);
886 break :blk ops.reg2.size() == 64;
887 } else {
888 // movabs rax, moffs64
889 const opc: u8 = if (ops.reg2.size() == 8) 0xa0 else 0xa1;
890 encoder.rex(.{
891 .w = ops.reg1.size() == 64,
892 });
893 encoder.opcode_1byte(opc);
894 break :blk ops.reg1.size() == 64;
895 }
896 };
897
898 if (is_64) {
1454 const imm: i64 = if (ops.reg1.size() == 64) blk: {
8991455 const payload = emit.mir.instructions.items(.data)[inst].payload;
900 const imm64 = emit.mir.extraData(Mir.Imm64, payload).data;
901 encoder.imm64(imm64.decode());
1456 const imm = emit.mir.extraData(Mir.Imm64, payload).data;
1457 break :blk @bitCast(i64, imm.decode());
1458 } else emit.mir.instructions.items(.data)[inst].imm;
1459 if (ops.flags == 0b00) {
1460 // movabs reg, imm64
1461 // OI
1462 return lowerToOiEnc(.mov, ops.reg1, imm, emit.code);
1463 }
1464 if (ops.reg1 == .none) {
1465 // movabs moffs64, rax
1466 // TD
1467 return lowerToTdEnc(.mov, imm, ops.reg2, emit.code);
9021468 } else {
903 const imm = emit.mir.instructions.items(.data)[inst].imm;
904 if (imm <= math.maxInt(i8)) {
905 encoder.imm8(@intCast(i8, imm));
906 } else if (imm <= math.maxInt(i16)) {
907 encoder.imm16(@intCast(i16, imm));
908 } else {
909 encoder.imm32(imm);
910 }
1469 // movabs rax, moffs64
1470 // FD
1471 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
9111472 }
9121473}
9131474
......@@ -916,34 +1477,10 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
9161477 assert(tag == .imul_complex);
9171478 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
9181479 switch (ops.flags) {
919 0b00 => {
920 const encoder = try Encoder.init(emit.code, 4);
921 encoder.rex(.{
922 .w = ops.reg1.size() == 64,
923 .r = ops.reg1.isExtended(),
924 .b = ops.reg2.isExtended(),
925 });
926 encoder.opcode_2byte(0x0f, 0xaf);
927 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
928 },
1480 0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code),
9291481 0b10 => {
9301482 const imm = emit.mir.instructions.items(.data)[inst].imm;
931 const opc: u8 = if (imm <= math.maxInt(i8)) 0x6b else 0x69;
932 const encoder = try Encoder.init(emit.code, 7);
933 encoder.rex(.{
934 .w = ops.reg1.size() == 64,
935 .r = ops.reg1.isExtended(),
936 .b = ops.reg1.isExtended(),
937 });
938 encoder.opcode_1byte(opc);
939 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
940 if (imm <= math.maxInt(i8)) {
941 encoder.imm8(@intCast(i8, imm));
942 } else if (imm <= math.maxInt(i16)) {
943 encoder.imm16(@intCast(i16, imm));
944 } else {
945 encoder.imm32(imm);
946 }
1483 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
9471484 },
9481485 else => return emit.fail("TODO implement imul", .{}),
9491486 }
......@@ -955,37 +1492,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
9551492 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
9561493 assert(ops.flags == 0b01);
9571494 const imm = emit.mir.instructions.items(.data)[inst].imm;
958
959 if (imm == 0) {
960 const encoder = try Encoder.init(emit.code, 3);
961 encoder.rex(.{
962 .w = ops.reg1.size() == 64,
963 .r = ops.reg1.isExtended(),
964 .b = ops.reg2.isExtended(),
965 });
966 encoder.opcode_1byte(0x8d);
967 encoder.modRm_indirectDisp0(ops.reg1.lowId(), ops.reg2.lowId());
968 } else if (imm <= math.maxInt(i8)) {
969 const encoder = try Encoder.init(emit.code, 4);
970 encoder.rex(.{
971 .w = ops.reg1.size() == 64,
972 .r = ops.reg1.isExtended(),
973 .b = ops.reg2.isExtended(),
974 });
975 encoder.opcode_1byte(0x8d);
976 encoder.modRm_indirectDisp8(ops.reg1.lowId(), ops.reg2.lowId());
977 encoder.disp8(@intCast(i8, imm));
978 } else {
979 const encoder = try Encoder.init(emit.code, 7);
980 encoder.rex(.{
981 .w = ops.reg1.size() == 64,
982 .r = ops.reg1.isExtended(),
983 .b = ops.reg2.isExtended(),
984 });
985 encoder.opcode_1byte(0x8d);
986 encoder.modRm_indirectDisp32(ops.reg1.lowId(), ops.reg2.lowId());
987 encoder.disp32(imm);
988 }
1495 return lowerToRmEnc(.lea, ops.reg1, RegisterOrMemory.mem(ops.reg2, imm), emit.code);
9891496}
9901497
9911498fn mirLeaRip(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -993,26 +1500,22 @@ fn mirLeaRip(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
9931500 assert(tag == .lea_rip);
9941501 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
9951502 const start_offset = emit.code.items.len;
996 const encoder = try Encoder.init(emit.code, 7);
997 encoder.rex(.{
998 .w = ops.reg1.size() == 64,
999 .r = ops.reg1.isExtended(),
1000 });
1001 encoder.opcode_1byte(0x8d);
1002 encoder.modRm_RIPDisp32(ops.reg1.lowId());
1503 try lowerToRmEnc(.lea, ops.reg1, RegisterOrMemory.rip(0), emit.code);
10031504 const end_offset = emit.code.items.len;
10041505 if (@truncate(u1, ops.flags) == 0b0) {
1506 // Backpatch the displacement
1507 // TODO figure out if this can be simplified
10051508 const payload = emit.mir.instructions.items(.data)[inst].payload;
10061509 const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode();
1007 encoder.disp32(@intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset + 4)));
1510 const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset));
1511 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);
10081512 } else {
10091513 const got_entry = emit.mir.instructions.items(.data)[inst].got_entry;
1010 encoder.disp32(0);
10111514 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
10121515 // TODO I think the reloc might be in the wrong place.
10131516 const decl = macho_file.active_decl.?;
10141517 try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
1015 .offset = @intCast(u32, end_offset),
1518 .offset = @intCast(u32, end_offset - 4),
10161519 .target = .{ .local = got_entry },
10171520 .addend = 0,
10181521 .subtractor = null,
......@@ -1031,12 +1534,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10311534 assert(tag == .call_extern);
10321535 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
10331536 const offset = blk: {
1034 const offset = @intCast(u32, emit.code.items.len + 1);
10351537 // callq
1036 const encoder = try Encoder.init(emit.code, 5);
1037 encoder.opcode_1byte(0xe8);
1038 encoder.imm32(0x0);
1039 break :blk offset;
1538 try lowerToDEnc(.call_near, 0, emit.code);
1539 break :blk @intCast(u32, emit.code.items.len) - 4;
10401540 };
10411541 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
10421542 // Add relocation to the decl.
......@@ -1206,3 +1706,230 @@ fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void {
12061706 .none => {},
12071707 }
12081708}
1709
1710fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
1711 assert(expected.len > 0);
1712 if (mem.eql(u8, expected, given)) return;
1713 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)});
1714 defer testing.allocator.free(expected_fmt);
1715 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)});
1716 defer testing.allocator.free(given_fmt);
1717 const idx = mem.indexOfDiff(u8, expected_fmt, given_fmt).?;
1718 var padding = try testing.allocator.alloc(u8, idx + 5);
1719 defer testing.allocator.free(padding);
1720 mem.set(u8, padding, ' ');
1721 std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{
1722 assembly,
1723 expected_fmt,
1724 given_fmt,
1725 padding,
1726 });
1727 return error.TestFailed;
1728}
1729
1730const TestEmitCode = struct {
1731 buf: std.ArrayList(u8),
1732 next: usize = 0,
1733
1734 fn init() TestEmitCode {
1735 return .{
1736 .buf = std.ArrayList(u8).init(testing.allocator),
1737 };
1738 }
1739
1740 fn deinit(emit: *TestEmitCode) void {
1741 emit.buf.deinit();
1742 emit.next = undefined;
1743 }
1744
1745 fn buffer(emit: *TestEmitCode) *std.ArrayList(u8) {
1746 emit.next = emit.buf.items.len;
1747 return &emit.buf;
1748 }
1749
1750 fn emitted(emit: TestEmitCode) []const u8 {
1751 return emit.buf.items[emit.next..];
1752 }
1753};
1754
1755test "lower MI encoding" {
1756 var code = TestEmitCode.init();
1757 defer code.deinit();
1758 try lowerToMiEnc(.mov, RegisterOrMemory.reg(.rax), 0x10, code.buffer());
1759 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", code.emitted(), "mov rax, 0x10");
1760 try lowerToMiEnc(.mov, RegisterOrMemory.mem(.r11, 0), 0x10, code.buffer());
1761 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", code.emitted(), "mov dword ptr [r11 + 0], 0x10");
1762 try lowerToMiEnc(.add, RegisterOrMemory.mem(.rdx, -8), 0x10, code.buffer());
1763 try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", code.emitted(), "add dword ptr [rdx - 8], 0x10");
1764 try lowerToMiEnc(.sub, RegisterOrMemory.mem(.r11, 0x10000000), 0x10, code.buffer());
1765 try expectEqualHexStrings(
1766 "\x41\x81\xab\x00\x00\x00\x10\x10\x00\x00\x00",
1767 code.emitted(),
1768 "sub dword ptr [r11 + 0x10000000], 0x10",
1769 );
1770 try lowerToMiEnc(.@"and", RegisterOrMemory.mem(null, 0x10000000), 0x10, code.buffer());
1771 try expectEqualHexStrings(
1772 "\x81\x24\x25\x00\x00\x00\x10\x10\x00\x00\x00",
1773 code.emitted(),
1774 "and dword ptr [ds:0x10000000], 0x10",
1775 );
1776 try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.r12, 0x10000000), 0x10, code.buffer());
1777 try expectEqualHexStrings(
1778 "\x41\x81\xA4\x24\x00\x00\x00\x10\x10\x00\x00\x00",
1779 code.emitted(),
1780 "and dword ptr [r12 + 0x10000000], 0x10",
1781 );
1782 try lowerToMiEnc(.mov, RegisterOrMemory.rip(0x10), 0x10, code.buffer());
1783 try expectEqualHexStrings(
1784 "\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
1785 code.emitted(),
1786 "mov [rip + 0x10], 0x10",
1787 );
1788}
1789
1790test "lower RM encoding" {
1791 var code = TestEmitCode.init();
1792 defer code.deinit();
1793 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), code.buffer());
1794 try expectEqualHexStrings("\x48\x8b\xc3", code.emitted(), "mov rax, rbx");
1795 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.r11, 0), code.buffer());
1796 try expectEqualHexStrings("\x49\x8b\x03", code.emitted(), "mov rax, qword ptr [r11 + 0]");
1797 try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(null, 0x10000000), code.buffer());
1798 try expectEqualHexStrings(
1799 "\x4C\x03\x1C\x25\x00\x00\x00\x10",
1800 code.emitted(),
1801 "add r11, qword ptr [ds:0x10000000]",
1802 );
1803 try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(null, 0x10000000), code.buffer());
1804 try expectEqualHexStrings(
1805 "\x44\x02\x24\x25\x00\x00\x00\x10",
1806 code.emitted(),
1807 "add r11b, byte ptr [ds:0x10000000]",
1808 );
1809 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.r13, 0x10000000), code.buffer());
1810 try expectEqualHexStrings(
1811 "\x4D\x2B\x9D\x00\x00\x00\x10",
1812 code.emitted(),
1813 "sub r11, qword ptr [r13 + 0x10000000]",
1814 );
1815 try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.r12, 0x10000000), code.buffer());
1816 try expectEqualHexStrings(
1817 "\x4D\x2B\x9C\x24\x00\x00\x00\x10",
1818 code.emitted(),
1819 "sub r11, qword ptr [r12 + 0x10000000]",
1820 );
1821 try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.rbp, -4), code.buffer());
1822 try expectEqualHexStrings("\x48\x8B\x45\xFC", code.emitted(), "mov rax, qword ptr [rbp - 4]");
1823 try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(0x10), code.buffer());
1824 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", code.emitted(), "lea rax, [rip + 0x10]");
1825}
1826
1827test "lower MR encoding" {
1828 var code = TestEmitCode.init();
1829 defer code.deinit();
1830 try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, code.buffer());
1831 try expectEqualHexStrings("\x48\x89\xd8", code.emitted(), "mov rax, rbx");
1832 try lowerToMrEnc(.mov, RegisterOrMemory.mem(.rbp, -4), .r11, code.buffer());
1833 try expectEqualHexStrings("\x4c\x89\x5d\xfc", code.emitted(), "mov qword ptr [rbp - 4], r11");
1834 try lowerToMrEnc(.add, RegisterOrMemory.mem(null, 0x10000000), .r12b, code.buffer());
1835 try expectEqualHexStrings(
1836 "\x44\x00\x24\x25\x00\x00\x00\x10",
1837 code.emitted(),
1838 "add byte ptr [ds:0x10000000], r12b",
1839 );
1840 try lowerToMrEnc(.add, RegisterOrMemory.mem(null, 0x10000000), .r12d, code.buffer());
1841 try expectEqualHexStrings(
1842 "\x44\x01\x24\x25\x00\x00\x00\x10",
1843 code.emitted(),
1844 "add dword ptr [ds:0x10000000], r12d",
1845 );
1846 try lowerToMrEnc(.sub, RegisterOrMemory.mem(.r11, 0x10000000), .r12, code.buffer());
1847 try expectEqualHexStrings(
1848 "\x4D\x29\xA3\x00\x00\x00\x10",
1849 code.emitted(),
1850 "sub qword ptr [r11 + 0x10000000], r12",
1851 );
1852 try lowerToMrEnc(.mov, RegisterOrMemory.rip(0x10), .r12, code.buffer());
1853 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", code.emitted(), "mov qword ptr [rip + 0x10], r12");
1854}
1855
1856test "lower OI encoding" {
1857 var code = TestEmitCode.init();
1858 defer code.deinit();
1859 try lowerToOiEnc(.mov, .rax, 0x1000000000000000, code.buffer());
1860 try expectEqualHexStrings(
1861 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
1862 code.emitted(),
1863 "movabs rax, 0x1000000000000000",
1864 );
1865 try lowerToOiEnc(.mov, .r11, 0x1000000000000000, code.buffer());
1866 try expectEqualHexStrings(
1867 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
1868 code.emitted(),
1869 "movabs r11, 0x1000000000000000",
1870 );
1871 try lowerToOiEnc(.mov, .r11d, 0x10000000, code.buffer());
1872 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", code.emitted(), "mov r11d, 0x10000000");
1873 try lowerToOiEnc(.mov, .r11w, 0x1000, code.buffer());
1874 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", code.emitted(), "mov r11w, 0x1000");
1875 try lowerToOiEnc(.mov, .r11b, 0x10, code.buffer());
1876 try expectEqualHexStrings("\x41\xB3\x10", code.emitted(), "mov r11b, 0x10");
1877}
1878
1879test "lower FD/TD encoding" {
1880 var code = TestEmitCode.init();
1881 defer code.deinit();
1882 try lowerToFdEnc(.mov, .rax, 0x1000000000000000, code.buffer());
1883 try expectEqualHexStrings(
1884 "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10",
1885 code.emitted(),
1886 "mov rax, ds:0x1000000000000000",
1887 );
1888 try lowerToFdEnc(.mov, .eax, 0x10000000, code.buffer());
1889 try expectEqualHexStrings("\xa1\x00\x00\x00\x10", code.emitted(), "mov eax, ds:0x10000000");
1890 try lowerToFdEnc(.mov, .ax, 0x1000, code.buffer());
1891 try expectEqualHexStrings("\x66\xa1\x00\x10", code.emitted(), "mov ax, ds:0x1000");
1892 try lowerToFdEnc(.mov, .al, 0x10, code.buffer());
1893 try expectEqualHexStrings("\xa0\x10", code.emitted(), "mov al, ds:0x10");
1894}
1895
1896test "lower M encoding" {
1897 var code = TestEmitCode.init();
1898 defer code.deinit();
1899 try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), code.buffer());
1900 try expectEqualHexStrings("\x41\xFF\xE4", code.emitted(), "jmp r12");
1901 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0), code.buffer());
1902 try expectEqualHexStrings("\x41\xFF\x24\x24", code.emitted(), "jmp qword ptr [r12]");
1903 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0x10), code.buffer());
1904 try expectEqualHexStrings("\x41\xFF\x64\x24\x10", code.emitted(), "jmp qword ptr [r12 + 0x10]");
1905 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0x1000), code.buffer());
1906 try expectEqualHexStrings(
1907 "\x41\xFF\xA4\x24\x00\x10\x00\x00",
1908 code.emitted(),
1909 "jmp qword ptr [r12 + 0x1000]",
1910 );
1911 try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(0x10), code.buffer());
1912 try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]");
1913 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer());
1914 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]");
1915 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), code.buffer());
1916 try expectEqualHexStrings("\x49\x0F\x97\xC3", code.emitted(), "seta r11b");
1917}
1918
1919test "lower O encoding" {
1920 var code = TestEmitCode.init();
1921 defer code.deinit();
1922 try lowerToOEnc(.pop, .r12, code.buffer());
1923 try expectEqualHexStrings("\x41\x5c", code.emitted(), "pop r12");
1924 try lowerToOEnc(.push, .r12w, code.buffer());
1925 try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");
1926}
1927
1928test "lower RMI encoding" {
1929 var code = TestEmitCode.init();
1930 defer code.deinit();
1931 try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.rbp, -8), 0x10, code.buffer());
1932 try expectEqualHexStrings("\x48\x69\x45\xF8\x10\x00\x00\x00", code.emitted(), "imul rax, [rbp - 8], 0x10");
1933 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer());
1934 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10");
1935}
src/arch/x86_64/Mir.zig+1
......@@ -140,6 +140,7 @@ pub const Inst = struct {
140140 mov_scale_src,
141141 mov_scale_dst,
142142 mov_scale_imm,
143
143144 lea,
144145 lea_scale_src,
145146 lea_scale_dst,