From a70b4068c6a8585da8b0a28326010807e5c427c1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 23 Dec 2021 10:01:03 +0100 Subject: [PATCH] stage2: add lowering to I encoding Examples include push imm32. --- src/arch/x86_64/Emit.zig | 90 +++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index c1331d7b51..c25785fa55 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -215,16 +215,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { // PUSH imm32 assert(tag == .push); const imm = emit.mir.instructions.items(.data)[inst].imm; - const opc: u8 = if (imm <= math.maxInt(i8)) 0x6a else 0x6b; - const encoder = try Encoder.init(emit.code, 2); - encoder.opcode_1byte(opc); - if (imm <= math.maxInt(i8)) { - encoder.imm8(@intCast(i8, imm)); - } else if (imm <= math.maxInt(i16)) { - encoder.imm16(@intCast(i16, imm)); - } else { - encoder.imm32(imm); - } + return lowerToIEnc(.push, imm, emit.code); }, 0b11 => unreachable, } @@ -422,31 +413,17 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .@"test"); const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { - 0b00 => blk: { + 0b00 => { if (ops.reg2 == .none) { // TEST r/m64, imm32 + // MI const imm = emit.mir.instructions.items(.data)[inst].imm; if (ops.reg1.to64() == .rax) { - // TODO reduce the size of the instruction if the immediate - // is smaller than 32 bits - const encoder = try Encoder.init(emit.code, 6); - encoder.rex(.{ - .w = true, - }); - encoder.opcode_1byte(0xa9); - encoder.imm32(imm); - break :blk; + // TEST rax, imm32 + // I + return lowerToIEnc(.@"test", imm, emit.code); } - const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7; - const encoder = try Encoder.init(emit.code, 7); - encoder.rex(.{ - .w = true, - .b = ops.reg1.isExtended(), - }); - encoder.opcode_1byte(opc); - encoder.modRm_direct(0, ops.reg1.lowId()); - encoder.imm8(@intCast(i8, imm)); - break :blk; + return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); } // TEST r/m64, r64 return emit.fail("TODO TEST r/m64, r64", .{}); @@ -463,16 +440,16 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { // RETF imm16 + // I const imm = emit.mir.instructions.items(.data)[inst].imm; - encoder.opcode_1byte(0xca); - encoder.imm16(@intCast(i16, imm)); + return lowerToIEnc(.ret_far, imm, emit.code); }, 0b01 => encoder.opcode_1byte(0xcb), // RETF 0b10 => { // RET imm16 + // I const imm = emit.mir.instructions.items(.data)[inst].imm; - encoder.opcode_1byte(0xc2); - encoder.imm16(@intCast(i16, imm)); + return lowerToIEnc(.ret_near, imm, emit.code); }, 0b11 => encoder.opcode_1byte(0xc3), // RET } @@ -493,6 +470,9 @@ const Tag = enum { call_near, push, pop, + @"test", + ret_near, + ret_far, }; const Encoding = enum { @@ -505,6 +485,9 @@ const Encoding = enum { /// OP r64 o, + /// OP imm32 + i, + /// OP r/m64, imm32 mi, @@ -541,9 +524,17 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 { .pop => 0x58, else => null, }, + .i => return switch (tag) { + .push => 0x68, + .@"test" => 0xa9, + .ret_near => 0xc2, + .ret_far => 0xca, + else => null, + }, .mi => return switch (tag) { .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81, .mov => 0xc7, + .@"test" => 0xf7, else => null, }, .mr => return switch (tag) { @@ -601,6 +592,7 @@ inline fn getModRmExt(tag: Tag) ?u3 { .call_near => 0x2, .push => 0x6, .pop => 0x0, + .@"test" => 0x0, else => null, }; } @@ -646,6 +638,36 @@ const RegisterOrMemory = union(enum) { } }; +fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { + var opc = getOpCode(tag, .i).?; + if (tag == .ret_far or tag == .ret_near) { + const encoder = try Encoder.init(code, 3); + encoder.opcode_1byte(opc); + encoder.imm16(@intCast(i16, imm)); + return; + } + if (immOpSize(imm) == 8) { + // TODO I think getOpCode should track this + switch (tag) { + .push => opc += 2, + .@"test" => opc -= 1, + else => return error.EmitFail, + } + } + const encoder = try Encoder.init(code, 5); + if (immOpSize(imm) == 16) { + encoder.opcode_1byte(0x66); + } + encoder.opcode_1byte(opc); + if (immOpSize(imm) == 8) { + encoder.imm8(@intCast(i8, imm)); + } else if (immOpSize(imm) == 16) { + encoder.imm16(@intCast(i16, imm)); + } else { + encoder.imm32(imm); + } +} + 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).?;