Commit b657956c44

Jakub Konka <kubkon@jakubkonka.com>
2021-12-23 09:47:38
stage2: add lowering to O encoding
Example includes push/pop register.
1 parent 603f826
Changed files (1)
src
arch
x86_64
src/arch/x86_64/Emit.zig
@@ -204,16 +204,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
     switch (ops.flags) {
         0b00 => {
             // PUSH/POP reg
-            const opc: u8 = switch (tag) {
-                .push => 0x50,
-                .pop => 0x58,
-                else => unreachable,
-            };
-            const encoder = try Encoder.init(emit.code, 2);
-            encoder.rex(.{
-                .b = ops.reg1.isExtended(),
-            });
-            encoder.opcode_withReg(opc, ops.reg1.lowId());
+            return lowerToOEnc(tag, ops.reg1, emit.code);
         },
         0b01 => {
             // PUSH/POP r/m64
@@ -240,22 +231,11 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
 }
 fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
     const callee_preserved_regs = bits.callee_preserved_regs;
-    // PUSH/POP reg
-    const opc: u8 = switch (tag) {
-        .push => 0x50,
-        .pop => 0x58,
-        else => unreachable,
-    };
-
     const regs = emit.mir.instructions.items(.data)[inst].regs_to_push_or_pop;
     if (tag == .push) {
         for (callee_preserved_regs) |reg, i| {
             if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
-            const encoder = try Encoder.init(emit.code, 2);
-            encoder.rex(.{
-                .b = reg.isExtended(),
-            });
-            encoder.opcode_withReg(opc, reg.lowId());
+            try lowerToOEnc(.push, reg, emit.code);
         }
     } else {
         // pop in the reverse direction
@@ -263,11 +243,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
         while (i > 0) : (i -= 1) {
             const reg = callee_preserved_regs[i - 1];
             if ((regs >> @intCast(u5, i - 1)) & 1 == 0) continue;
-            const encoder = try Encoder.init(emit.code, 2);
-            encoder.rex(.{
-                .b = reg.isExtended(),
-            });
-            encoder.opcode_withReg(opc, reg.lowId());
+            try lowerToOEnc(.pop, reg, emit.code);
         }
     }
 }
@@ -526,6 +502,9 @@ const Encoding = enum {
     /// OP r/m64
     m,
 
+    /// OP r64
+    o,
+
     /// OP r/m64, imm32
     mi,
 
@@ -557,6 +536,11 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 {
             .pop => 0x8f,
             else => null,
         },
+        .o => return switch (tag) {
+            .push => 0x50,
+            .pop => 0x58,
+            else => null,
+        },
         .mi => return switch (tag) {
             .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81,
             .mov => 0xc7,
@@ -662,6 +646,20 @@ const RegisterOrMemory = union(enum) {
     }
 };
 
+fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
+    if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal?
+    const opc = getOpCode(tag, .o).?;
+    const encoder = try Encoder.init(code, 3);
+    if (reg.size() == 16) {
+        encoder.opcode_1byte(0x66);
+    }
+    encoder.rex(.{
+        .w = false,
+        .b = reg.isExtended(),
+    });
+    encoder.opcode_withReg(opc, reg.lowId());
+}
+
 fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
     const opc = getOpCode(tag, .d).?;
     const encoder = try Encoder.init(code, 5);
@@ -1727,3 +1725,12 @@ test "lower M encoding" {
     try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer());
     try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]");
 }
+
+test "lower O encoding" {
+    var code = TestEmitCode.init();
+    defer code.deinit();
+    try lowerToOEnc(.pop, .r12, code.buffer());
+    try expectEqualHexStrings("\x41\x5c", code.emitted(), "pop r12");
+    try lowerToOEnc(.push, .r12w, code.buffer());
+    try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");
+}