aboutsummaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-02-07 20:11:42 +0100
committerJakub Konka <kubkon@jakubkonka.com>2022-02-07 20:13:29 +0100
commitbecbf446d3d33fd73a7c1567e89a98a9b191d4e8 (patch)
treeddf0920aa7d75276e55b91119217231a0b3d10f8 /src/arch
parent7a9b9df80e9799421393f476ea10332ba35ec258 (diff)
downloadzig-becbf446d3d33fd73a7c1567e89a98a9b191d4e8.tar.gz
zig-becbf446d3d33fd73a7c1567e89a98a9b191d4e8.zip
stage2,x64: impl lowering of shift ops in Emit
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/x86_64/CodeGen.zig39
-rw-r--r--src/arch/x86_64/Emit.zig156
-rw-r--r--src/arch/x86_64/Mir.zig55
3 files changed, 203 insertions, 47 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,
@@ -213,6 +189,37 @@ pub const Inst = struct {
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]
imul,