authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 10:01:03+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 10:22:23+01:00
loga70b4068c6a8585da8b0a28326010807e5c427c1
tree338f68bc6243395ab6e91a4bd2426623e9c5a106
parentb657956c440b355abab9fdf01a1517f99dd1ec90

stage2: add lowering to I encoding

Examples include push imm32.

1 files changed, 56 insertions(+), 34 deletions(-)

src/arch/x86_64/Emit.zig+56-34
......@@ -215,16 +215,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
215215 // PUSH imm32
216216 assert(tag == .push);
217217 const imm = emit.mir.instructions.items(.data)[inst].imm;
218 const opc: u8 = if (imm <= math.maxInt(i8)) 0x6a else 0x6b;
219 const encoder = try Encoder.init(emit.code, 2);
220 encoder.opcode_1byte(opc);
221 if (imm <= math.maxInt(i8)) {
222 encoder.imm8(@intCast(i8, imm));
223 } else if (imm <= math.maxInt(i16)) {
224 encoder.imm16(@intCast(i16, imm));
225 } else {
226 encoder.imm32(imm);
227 }
218 return lowerToIEnc(.push, imm, emit.code);
228219 },
229220 0b11 => unreachable,
230221 }
......@@ -422,31 +413,17 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
422413 assert(tag == .@"test");
423414 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
424415 switch (ops.flags) {
425 0b00 => blk: {
416 0b00 => {
426417 if (ops.reg2 == .none) {
427418 // TEST r/m64, imm32
419 // MI
428420 const imm = emit.mir.instructions.items(.data)[inst].imm;
429421 if (ops.reg1.to64() == .rax) {
430 // TODO reduce the size of the instruction if the immediate
431 // is smaller than 32 bits
432 const encoder = try Encoder.init(emit.code, 6);
433 encoder.rex(.{
434 .w = true,
435 });
436 encoder.opcode_1byte(0xa9);
437 encoder.imm32(imm);
438 break :blk;
422 // TEST rax, imm32
423 // I
424 return lowerToIEnc(.@"test", imm, emit.code);
439425 }
440 const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7;
441 const encoder = try Encoder.init(emit.code, 7);
442 encoder.rex(.{
443 .w = true,
444 .b = ops.reg1.isExtended(),
445 });
446 encoder.opcode_1byte(opc);
447 encoder.modRm_direct(0, ops.reg1.lowId());
448 encoder.imm8(@intCast(i8, imm));
449 break :blk;
426 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
450427 }
451428 // TEST r/m64, r64
452429 return emit.fail("TODO TEST r/m64, r64", .{});
......@@ -463,16 +440,16 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
463440 switch (ops.flags) {
464441 0b00 => {
465442 // RETF imm16
443 // I
466444 const imm = emit.mir.instructions.items(.data)[inst].imm;
467 encoder.opcode_1byte(0xca);
468 encoder.imm16(@intCast(i16, imm));
445 return lowerToIEnc(.ret_far, imm, emit.code);
469446 },
470447 0b01 => encoder.opcode_1byte(0xcb), // RETF
471448 0b10 => {
472449 // RET imm16
450 // I
473451 const imm = emit.mir.instructions.items(.data)[inst].imm;
474 encoder.opcode_1byte(0xc2);
475 encoder.imm16(@intCast(i16, imm));
452 return lowerToIEnc(.ret_near, imm, emit.code);
476453 },
477454 0b11 => encoder.opcode_1byte(0xc3), // RET
478455 }
......@@ -493,6 +470,9 @@ const Tag = enum {
493470 call_near,
494471 push,
495472 pop,
473 @"test",
474 ret_near,
475 ret_far,
496476};
497477
498478const Encoding = enum {
......@@ -505,6 +485,9 @@ const Encoding = enum {
505485 /// OP r64
506486 o,
507487
488 /// OP imm32
489 i,
490
508491 /// OP r/m64, imm32
509492 mi,
510493
......@@ -541,9 +524,17 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 {
541524 .pop => 0x58,
542525 else => null,
543526 },
527 .i => return switch (tag) {
528 .push => 0x68,
529 .@"test" => 0xa9,
530 .ret_near => 0xc2,
531 .ret_far => 0xca,
532 else => null,
533 },
544534 .mi => return switch (tag) {
545535 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81,
546536 .mov => 0xc7,
537 .@"test" => 0xf7,
547538 else => null,
548539 },
549540 .mr => return switch (tag) {
......@@ -601,6 +592,7 @@ inline fn getModRmExt(tag: Tag) ?u3 {
601592 .call_near => 0x2,
602593 .push => 0x6,
603594 .pop => 0x0,
595 .@"test" => 0x0,
604596 else => null,
605597 };
606598}
......@@ -646,6 +638,36 @@ const RegisterOrMemory = union(enum) {
646638 }
647639};
648640
641fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
642 var opc = getOpCode(tag, .i).?;
643 if (tag == .ret_far or tag == .ret_near) {
644 const encoder = try Encoder.init(code, 3);
645 encoder.opcode_1byte(opc);
646 encoder.imm16(@intCast(i16, imm));
647 return;
648 }
649 if (immOpSize(imm) == 8) {
650 // TODO I think getOpCode should track this
651 switch (tag) {
652 .push => opc += 2,
653 .@"test" => opc -= 1,
654 else => return error.EmitFail,
655 }
656 }
657 const encoder = try Encoder.init(code, 5);
658 if (immOpSize(imm) == 16) {
659 encoder.opcode_1byte(0x66);
660 }
661 encoder.opcode_1byte(opc);
662 if (immOpSize(imm) == 8) {
663 encoder.imm8(@intCast(i8, imm));
664 } else if (immOpSize(imm) == 16) {
665 encoder.imm16(@intCast(i16, imm));
666 } else {
667 encoder.imm32(imm);
668 }
669}
670
649671fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
650672 if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal?
651673 const opc = getOpCode(tag, .o).?;