From becbf446d3d33fd73a7c1567e89a98a9b191d4e8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 20:11:42 +0100 Subject: [PATCH] stage2,x64: impl lowering of shift ops in Emit --- src/arch/x86_64/CodeGen.zig | 39 +++++++-- src/arch/x86_64/Emit.zig | 156 ++++++++++++++++++++++++++++++++---- src/arch/x86_64/Mir.zig | 55 +++++++------ src/codegen.zig | 1 - 4 files changed, 203 insertions(+), 48 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e2ae339bae..2642d5ce15 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2037,17 +2037,42 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; }, .register => |reg| { - // 1. Shift by struct_field_offset. - // 2. Mask with reg.size() - struct_field_size - // 3. Return in register - - // TODO check if register can be re-used self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); - const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() }); - // TODO shift here + const dst_mcv = blk: { + if (self.reuseOperand(inst, operand, 0, mcv)) { + break :blk mcv; + } else { + const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() }); + break :blk dst_mcv; + } + }; + // Shift by struct_field_offset. + const shift_amount = @intCast(u8, struct_field_offset * 8); + if (shift_amount > 0) { + if (shift_amount == 1) { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + }).encode(), + .data = undefined, + }); + } else { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift_amount }, + }); + } + } + + // Mask with reg.size() - struct_field_size const mask_shift = @intCast(u6, (64 - struct_field_ty.bitSize(self.target.*))); const mask = (~@as(u64, 0)) >> mask_shift; try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index be26354402..3f221f0f19 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -133,6 +133,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .lea => try emit.mirLea(inst), .lea_pie => try emit.mirLeaPie(inst), + .shl => try emit.mirShift(.shl, inst), + .sal => try emit.mirShift(.sal, inst), + .shr => try emit.mirShift(.shr, inst), + .sar => try emit.mirShift(.sar, inst), + .imul_complex => try emit.mirIMulComplex(inst), .push => try emit.mirPushPop(.push, inst), @@ -653,6 +658,31 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); } +fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + switch (ops.flags) { + 0b00 => { + // sal reg1, 1 + // M1 + return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + }, + 0b01 => { + // sal reg1, .cl + // MC + return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + }, + 0b10 => { + // sal reg1, imm8 + // MI + const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); + return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + }, + 0b11 => { + return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); + }, + } +} + fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .imul_complex); @@ -743,13 +773,13 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { emit.code, ); const end_offset = emit.code.items.len; - const reloc_type = switch (ops.flags) { - 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), - 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), - else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), - }; const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index; if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + const reloc_type = switch (ops.flags) { + 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), + 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), + else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), + }; const decl = macho_file.active_decl.?; try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ .offset = @intCast(u32, end_offset - 4), @@ -1064,6 +1094,10 @@ const Tag = enum { setng, setnle, setg, + shl, + sal, + shr, + sar, fn isSetCC(tag: Tag) bool { return switch (tag) { @@ -1119,9 +1153,18 @@ const Encoding = enum { /// OP imm32 i, + /// OP r/m64, 1 + m1, + + /// OP r/m64, .cl + mc, + /// OP r/m64, imm32 mi, + /// OP r/m64, imm8 + mi8, + /// OP r/m64, r64 mr, @@ -1230,12 +1273,25 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .ret_far => OpCode.oneByte(0xca), else => null, }, + .m1 => return switch (tag) { + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1), + else => null, + }, + .mc => return switch (tag) { + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3), + else => null, + }, .mi => return switch (tag) { .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), else => null, }, + .mi8 => return switch (tag) { + .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83), + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1), + else => null, + }, .mr => return switch (tag) { .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01), @@ -1331,6 +1387,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { .setnle, .setg, => 0x0, + .shl, + .sal, + => 0x4, + .shr => 0x5, + .sar => 0x7, else => null, }; } @@ -1528,8 +1589,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { encoder.imm32(@bitCast(i32, imm)); } -fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { - const opc = getOpCode(tag, .m, false).?; +fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; const modrm_ext = getModRmExt(tag).?; switch (reg_or_mem) { .register => |reg| { @@ -1537,11 +1598,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) if (reg.size() == 16) { encoder.prefix16BitMode(); } + const wide = if (tag == .jmp_near) false else setRexWRegister(reg); encoder.rex(.{ - .w = switch (reg) { - .ah, .bh, .ch, .dh => true, - else => false, - }, + .w = wide, .b = reg.isExtended(), }); opc.encode(encoder); @@ -1553,8 +1612,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) encoder.prefix16BitMode(); } if (mem_op.base) |base| { + const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr; encoder.rex(.{ - .w = false, + .w = wide, .b = base.isExtended(), }); } @@ -1564,6 +1624,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) } } +fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .m, code); +} + +fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .m1, code); +} + +fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .mc, code); +} + fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { return lowerToTdFdEnc(tag, reg, moffs, code, true); } @@ -1614,9 +1686,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Inn } } -fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { +fn lowerToMiXEnc( + tag: Tag, + reg_or_mem: RegisterOrMemory, + imm: u32, + enc: Encoding, + code: *std.ArrayList(u8), +) InnerError!void { const modrm_ext = getModRmExt(tag).?; - const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?; + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 7); @@ -1632,7 +1710,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr }); opc.encode(encoder); encoder.modRm_direct(modrm_ext, dst_reg.lowId()); - encodeImm(encoder, imm, dst_reg.size()); + encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size()); }, .memory => |dst_mem| { const encoder = try Encoder.init(code, 12); @@ -1651,11 +1729,19 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr } opc.encode(encoder); dst_mem.encode(encoder, modrm_ext); - encodeImm(encoder, imm, dst_mem.ptr_size.size()); + encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size()); }, } } +fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code); +} + +fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code); +} + fn lowerToRmEnc( tag: Tag, reg: Register, @@ -1902,6 +1988,9 @@ test "lower MI encoding" { emit.lowered(), "mov qword ptr [rcx*2 + 0x10000000], 0x10", ); + + try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code()); + try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10"); } test "lower RM encoding" { @@ -2100,6 +2189,41 @@ test "lower M encoding" { try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b"); } +test "lower M1 and MC encodings" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code()); + try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code()); + try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code()); + try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code()); + try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code()); + try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code()); + try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -0x10)), + .base = .rbp, + }), emit.code()); + try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -0x10)), + .base = .rbp, + }), emit.code()); + try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1"); + + try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code()); + try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl"); + try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code()); + try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl"); + + try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code()); + try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl"); +} + test "lower O encoding" { var emit = TestEmit.init(); defer emit.deinit(); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 2e8a9cf332..aaabcab04d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -142,30 +142,6 @@ pub const Inst = struct { rcr_scale_dst, rcr_scale_imm, rcr_mem_index_imm, - shl, - shl_mem_imm, - shl_scale_src, - shl_scale_dst, - shl_scale_imm, - shl_mem_index_imm, - sal, - sal_mem_imm, - sal_scale_src, - sal_scale_dst, - sal_scale_imm, - sal_mem_index_imm, - shr, - shr_mem_imm, - shr_scale_src, - shr_scale_dst, - shr_scale_imm, - shr_mem_index_imm, - sar, - sar_mem_imm, - sar_scale_src, - sar_scale_dst, - sar_scale_imm, - sar_mem_index_imm, sbb, sbb_mem_imm, sbb_scale_src, @@ -212,6 +188,37 @@ pub const Inst = struct { /// * `Data` contains `linker_sym_index` lea_pie, + /// ops flags: form: + /// 0b00 reg1, 1 + /// 0b01 reg1, .cl + /// 0b10 reg1, imm8 + /// Notes: + /// * If flags == 0b10, uses `imm`. + shl, + shl_mem_imm, + shl_scale_src, + shl_scale_dst, + shl_scale_imm, + shl_mem_index_imm, + sal, + sal_mem_imm, + sal_scale_src, + sal_scale_dst, + sal_scale_imm, + sal_mem_index_imm, + shr, + shr_mem_imm, + shr_scale_src, + shr_scale_dst, + shr_scale_imm, + shr_mem_index_imm, + sar, + sar_mem_imm, + sar_scale_src, + sar_scale_dst, + sar_scale_imm, + sar_mem_index_imm, + /// ops flags: form: /// 0bX0 reg1 /// 0bX1 [reg1 + imm32] diff --git a/src/codegen.zig b/src/codegen.zig index bcd36358b1..059d2adc14 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -372,7 +372,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Struct => { - // TODO debug info const struct_obj = typed_value.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { return Result{