diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-03-09 20:32:07 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-03-11 20:05:49 +0100 |
| commit | 32708dd6e2f16b4b688b2deed22253ea36233c91 (patch) | |
| tree | a2c1eaa13120f9f01c685f8e562241e3a053f2d4 /src | |
| parent | 9658ab676643ef5c2457ac4908c180e05dc7f729 (diff) | |
| download | zig-32708dd6e2f16b4b688b2deed22253ea36233c91.tar.gz zig-32708dd6e2f16b4b688b2deed22253ea36233c91.zip | |
x86_64: add RM and MR helpers to codegen
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 419 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 86 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 147 | ||||
| -rw-r--r-- | src/arch/x86_64/bits.zig | 11 |
4 files changed, 397 insertions, 266 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f5ee4d99eb..4441e63aba 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -491,6 +491,72 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Imme }); } +fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .m_sib, + .rip => .m_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .m_sib => .{ .payload = try self.addExtra(Mir.MemorySib.encode(m)) }, + .m_rip => .{ .payload = try self.addExtra(Mir.MemoryRip.encode(m)) }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + +fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .rm_sib, + .rip => .rm_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .rm_sib => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemorySib.encode(m)), + } }, + .rm_rip => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemoryRip.encode(m)), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + +fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void { + const ops: Mir.Inst.Ops = switch (m) { + .sib => .mr_sib, + .rip => .mr_rip, + else => unreachable, + }; + const data: Mir.Inst.Data = switch (ops) { + .mr_sib => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemorySib.encode(m)), + } }, + .mr_rip => .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.MemoryRip.encode(m)), + } }, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = ops, + .data = data, + }); +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -1741,23 +1807,10 @@ fn genIntMulDivOpMir( switch (factor) { .register => |reg| try self.asmRegister(tag, reg), - .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg2 = .rbp, - // .flags = switch (abi_size) { - // 1 => 0b00, - // 2 => 0b01, - // 4 => 0b10, - // 8 => 0b11, - // else => unreachable, - // }, - // }), - // .data = .{ .disp = -off }, - // }); - }, + .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + })), else => unreachable, } } @@ -2222,19 +2275,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const addr_reg = try self.register_manager.allocReg(null, gp); switch (slice_mcv) { - .stack_offset => |off| { - _ = off; - // mov reg, [rbp - 8] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -@intCast(i32, off) }, - // }); - }, + .stack_offset => |off| try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })), else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}), } // TODO we could allocate register here, but need to expect addr register and potentially @@ -2309,27 +2353,16 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { array_ty.abiAlignment(self.target.*), )); try self.genSetStack(array_ty, off, array, .{}); - // lea reg, [rbp] - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })); }, .stack_offset => |off| { - _ = off; - // lea reg, [rbp] - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, addr_reg.to64(), Memory.sib(.qword, .{ + .base = .rbp, + .disp = -off, + })); }, .memory, .linker_load => { try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); @@ -2366,7 +2399,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); const elem_ty = ptr_ty.elemType2(); - const elem_abi_size = elem_ty.abiSize(self.target.*); + const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); const index_lock: ?RegisterLock = switch (index) { @@ -2386,16 +2419,14 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { if (elem_abi_size > 8) { return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); } else { - // mov dst_mcv, [dst_mcv] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), - // .reg2 = dst_mcv.register, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(dst_mcv.register, elem_abi_size), + Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{ + .base = dst_mcv.register, + .disp = 0, + }), + ); break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; } }; @@ -2622,7 +2653,7 @@ fn reuseOperand( fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); - const abi_size = elem_ty.abiSize(self.target.*); + const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2649,17 +2680,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .eflags => unreachable, .register => |dst_reg| { - _ = dst_reg; - // mov dst_reg, [reg] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), - // .reg2 = reg, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + try self.asmRegisterMemory( + .mov, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg, .disp = 0 }), + ); }, .stack_offset => |off| { if (abi_size <= 8) { @@ -2874,17 +2899,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); - // to get the actual address of the value we want to modify we have to go through the GOT - // mov reg, [reg] - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = addr_reg.to64(), - // .reg2 = addr_reg.to64(), - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); + // To get the actual address of the value we want to modify we have to go through the GOT + try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ + .base = addr_reg.to64(), + .disp = 0, + })); const new_ptr = MCValue{ .register = addr_reg.to64() }; @@ -2936,16 +2955,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type defer self.register_manager.unlockReg(tmp_reg_lock); try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); + try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib(.qword, .{ + .base = tmp_reg, + .disp = 0, + })); - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = tmp_reg, - // .reg2 = tmp_reg, - // .flags = 0b01, - // }), - // .data = .{ .disp = 0 }, - // }); return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } @@ -3603,15 +3617,11 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + mir_tag, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, } }, @@ -3629,16 +3639,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .dead, .unreach => unreachable, .register_overflow => unreachable, .register => |src_reg| { - _ = src_reg; - // _ = try self.addInst(.{ - // .tag = mir_tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rbp, - // .reg2 = registerAlias(src_reg, abi_size), - // .flags = 0b10, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), registerAlias(src_reg, abi_size)); }, .immediate => |imm| { _ = imm; @@ -3738,16 +3742,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M } }, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = .rbp, - // .flags = 0b01, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory( + .imul, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), + ); }, .memory => { return self.fail("TODO implement x86 multiply source memory", .{}); @@ -3770,17 +3769,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); - _ = src_reg; - // multiply into dst_reg - // register, register - // _ = try self.addInst(.{ - // .tag = .imul_complex, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = registerAlias(dst_reg, abi_size), - // .reg2 = registerAlias(src_reg, abi_size), - // }), - // .data = undefined, - // }); + try self.asmRegisterRegister( + .imul, + registerAlias(dst_reg, abi_size), + registerAlias(src_reg, abi_size), + ); // copy dst_reg back out return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); }, @@ -4006,11 +3999,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (info.stack_byte_count > 0) { // Adjust the stack - // _ = try self.addInst(.{ - // .tag = .sub, - // .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), - // .data = .{ .imm = info.stack_byte_count }, - // }); + try self.asmRegisterImmediate(.sub, .rsp, Immediate.u(info.stack_byte_count)); } // Due to incremental compilation, how function calls are generated depends @@ -4161,7 +4150,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -4197,7 +4186,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -4738,7 +4727,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { const jmp_target = @intCast(u32, self.mir_instructions.len); try self.genBody(body); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = jmp_target }, }); @@ -5035,7 +5024,7 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { .jcc => { self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; }, - .jmp => { + .jmp_reloc => { self.mir_instructions.items(.data)[reloc].inst = next_inst; }, else => unreachable, @@ -5078,7 +5067,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); // Leave the jump offset undefined const jmp_reloc = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = undefined }, }); @@ -5247,7 +5236,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { - const abi_size = ty.abiSize(self.target.*); + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, .unreach, .none => return, @@ -5325,36 +5314,25 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .{ty.fmtDebug()}, ), }; - _ = tag; - _ = reg; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = switch (ty.tag()) { - // .f32 => .esp, - // .f64 => .rsp, - // else => unreachable, - // }, - // .reg2 = reg.to128(), - // .flags = 0b01, - // }), - // .data = .{ .disp = -stack_offset }, - // }); - return; + // TODO verify this + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ + .base = .rsp, + .disp = -stack_offset, + }), reg.to128()); } return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); }, else => { - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rsp, - // .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - // .flags = 0b10, - // }), - // .data = .{ .disp = -stack_offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rsp, + .disp = -stack_offset, + }), registerAlias(reg, abi_size)); }, } }, @@ -5507,25 +5485,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl switch (ty.zigTypeTag()) { .Float => { if (intrinsicsAllowed(self.target.*, ty)) { - // const tag: Mir.Inst.Tag = switch (ty.tag()) { - // .f32 => Mir.Inst.Tag.mov_f32, - // .f64 => Mir.Inst.Tag.mov_f64, - // else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), - // }; - // _ = try self.addInst(.{ - // .tag = tag, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = switch (ty.tag()) { - // .f32 => base_reg.to32(), - // .f64 => base_reg.to64(), - // else => unreachable, - // }, - // .reg2 = reg.to128(), - // .flags = 0b01, - // }), - // .data = .{ .disp = -stack_offset }, - // }); - return; + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .movss, + .f64 => .movsd, + else => return self.fail( + "TODO genSetStack for register for type {}", + .{ty.fmtDebug()}, + ), + }; + const ptr_size: Memory.PtrSize = switch (ty.tag()) { + .f32 => .dword, + .f64 => .qword, + else => unreachable, + }; + return self.asmMemoryRegister(tag, Memory.sib(ptr_size, .{ + .base = base_reg.to64(), + .disp = -stack_offset, + }), reg.to128()); } return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); @@ -5590,16 +5566,10 @@ fn genInlineMemcpyRegisterRegister( var remainder = abi_size; while (remainder > 0) { const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg, - // .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - // .flags = 0b10, - // }), - // .data = .{ .disp = -next_offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{ + .base = dst_reg, + .disp = -next_offset, + }), registerAlias(tmp_reg, nearest_power_of_two)); if (nearest_power_of_two > 1) { try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ @@ -5611,15 +5581,10 @@ fn genInlineMemcpyRegisterRegister( next_offset -= nearest_power_of_two; } } else { - // _ = try self.addInst(.{ - // .tag = .mov, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_reg, - // .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), - // .flags = 0b10, - // }), - // .data = .{ .disp = -offset }, - // }); + try self.asmMemoryRegister(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = dst_reg, + .disp = -offset, + }), registerAlias(src_reg, abi_size)); } } @@ -5660,15 +5625,10 @@ fn genInlineMemcpy( try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); }, .ptr_stack_offset, .stack_offset => |off| { - _ = off; - // _ = try self.addInst(.{ - // .tag = .lea, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = dst_addr_reg.to64(), - // .reg2 = opts.dest_stack_base orelse .rbp, - // }), - // .data = .{ .disp = -off }, - // }); + try self.asmRegisterMemory(.lea, dst_addr_reg.to64(), Memory.sib(.qword, .{ + .base = opts.dest_stack_base orelse .rbp, + .disp = -off, + })); }, .register => |reg| { try self.asmRegisterRegister( @@ -5754,7 +5714,7 @@ fn genInlineMemcpy( try self.asmRegisterImmediate(.sub, count_reg, Immediate.u(1)); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = loop_start }, }); @@ -5857,7 +5817,7 @@ fn genInlineMemset( try self.asmRegisterImmediate(.sub, index_reg, Immediate.u(1)); _ = try self.addInst(.{ - .tag = .jmp, + .tag = .jmp_reloc, .ops = .inst, .data = .{ .inst = loop_start }, }); @@ -6045,19 +6005,20 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // .data = .{ .disp = @intCast(i32, x) }, // }); } else { - // If this is RAX, we can use a direct load. - // Otherwise, we need to load the address, then indirectly load the value. if (reg.to64() == .rax) { - // movabs rax, ds:moffs64 - // const payload = try self.addExtra(Mir.Imm64.encode(x)); - // _ = try self.addInst(.{ - // .tag = .movabs, - // .ops = Mir.Inst.Ops.encode(.{ - // .reg1 = .rax, - // .flags = 0b01, // imm64 will become moffs64 - // }), - // .data = .{ .payload = payload }, - // }); + // If this is RAX, we can use a direct load. + // Otherwise, we need to load the address, then indirectly load the value. + var moffs: Mir.MemoryMoffs = .{ + .seg = @enumToInt(Register.ds), + .msb = undefined, + .lsb = undefined, + }; + moffs.encodeOffset(x); + _ = try self.addInst(.{ + .tag = .mov_moffs, + .ops = .rax_moffs, + .data = .{ .payload = try self.addExtra(moffs) }, + }); } else { // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. try self.genSetReg(ty, reg, MCValue{ .immediate = x }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 4eff2d11a0..fc1e345a5a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -73,6 +73,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .adc, .add, .@"and", + .call, .cbw, .cwde, .cdqe, @@ -86,6 +87,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .idiv, .imul, .int3, + .jmp, + .lea, .mov, .movzx, .mul, @@ -115,9 +118,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .ucomisd, => try emit.mirEncodeGeneric(tag, inst), - .call, - .jmp, - => try emit.mirCallJmp(inst), + .jmp_reloc => try emit.mirJmpReloc(inst), + + .mov_moffs => try emit.mirMovMoffs(inst), .movsx => try emit.mirMovsx(inst), .cmovcc => try emit.mirCmovcc(inst), @@ -130,8 +133,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .push_regs => try emit.mirPushPopRegisterList(.push, inst), .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), - - else => return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag}), } } @@ -212,6 +213,34 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }, }; }, + .m_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; + operands[0] = .{ .mem = Mir.MemorySib.decode(msib) }; + }, + .m_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; + operands[0] = .{ .mem = Mir.MemoryRip.decode(mrip) }; + }, + .rm_sib, .mr_sib => { + const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; + const op1 = .{ .reg = data.rx.r1 }; + const op2 = .{ .mem = Mir.MemorySib.decode(msib) }; + switch (ops) { + .rm_sib => operands[0..2].* = .{ op1, op2 }, + .mr_sib => operands[0..2].* = .{ op2, op1 }, + else => unreachable, + } + }, + .rm_rip, .mr_rip => { + const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; + const op1 = .{ .reg = data.rx.r1 }; + const op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; + switch (ops) { + .rm_rip => operands[0..2].* = .{ op1, op2 }, + .mr_rip => operands[0..2].* = .{ op2, op1 }, + else => unreachable, + } + }, else => unreachable, // TODO } @@ -223,6 +252,29 @@ fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE }); } +fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst]; + const payload = emit.mir.instructions.items(.data)[inst].payload; + const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data; + const seg = @intToEnum(Register, moffs.seg); + const offset = moffs.decodeOffset(); + switch (ops) { + .rax_moffs => { + try emit.encode(.mov, .{ + .op1 = .{ .reg = .rax }, + .op2 = .{ .mem = Memory.moffs(seg, offset) }, + }); + }, + .moffs_rax => { + try emit.encode(.mov, .{ + .op1 = .{ .mem = Memory.moffs(seg, offset) }, + .op2 = .{ .reg = .rax }, + }); + }, + else => unreachable, + } +} + fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; const data = emit.mir.instructions.items(.data)[inst]; @@ -302,19 +354,13 @@ fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirCallJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const mnemonic: Instruction.Mnemonic = switch (tag) { - .call => .call, - .jmp => .jmp, - else => unreachable, - }; +fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst]; switch (ops) { .inst => { const target = emit.mir.instructions.items(.data)[inst].inst; const source = emit.code.items.len; - try emit.encode(mnemonic, .{ + try emit.encode(.jmp, .{ .op1 = .{ .imm = Immediate.s(0) }, }); try emit.relocs.append(emit.bin_file.allocator, .{ @@ -324,19 +370,7 @@ fn mirCallJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { .length = 5, }); }, - .r => { - const reg = emit.mir.instructions.items(.data)[inst].r; - try emit.encode(mnemonic, .{ - .op1 = .{ .reg = reg }, - }); - }, - .imm_s => { - const imm = emit.mir.instructions.items(.data)[inst].imm_s; - try emit.encode(mnemonic, .{ - .op1 = .{ .imm = Immediate.s(imm) }, - }); - }, - else => unreachable, // TODO + else => unreachable, } } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 02bc70614d..40fd1953de 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -17,6 +17,7 @@ const encoder = @import("encoder.zig"); const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const IntegerBitSet = std.bit_set.IntegerBitSet; +const Memory = bits.Memory; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -135,6 +136,12 @@ pub const Inst = struct { /// Conditional move cmovcc, + /// Mov absolute to/from memory wrt segment register to/from rax + mov_moffs, + + /// Jump with relocation to another local MIR instruction + jmp_reloc, + /// End of prologue dbg_prologue_end, /// Start of epilogue @@ -186,24 +193,48 @@ pub const Inst = struct { /// Relative displacement operand. /// Uses `rel` payload. rel, - /// Register, memory operands. + /// Register, memory (SIB) operands. + /// Uses `rx` payload. + rm_sib, + /// Register, memory (RIP) operands. /// Uses `rx` payload. - rm, + rm_rip, /// Register, memory, immediate (unsigned) operands /// Uses `rx` payload. rmi_u, /// Register, memory, immediate (sign-extended) operands /// Uses `rx` payload. rmi_s, - /// Memory, immediate (unsigned) operands. - /// Uses `payload` payload. - mi_u, - /// Memory, immediate (sign-extend) operands. - /// Uses `payload` payload. - mi_s, - /// Memory, register operands. - /// Uses `payload` payload. - mr, + /// Single memory (SIB) operand. + /// Uses `payload` with extra data of type `MemorySib`. + m_sib, + /// Single memory (RIP) operand. + /// Uses `payload` with extra data of type `MemoryRip`. + m_rip, + /// Memory (SIB), immediate (unsigned) operands. + /// Uses `xi_u` payload with extra data of type `MemorySib`. + mi_u_sib, + /// Memory (RIP), immediate (unsigned) operands. + /// Uses `xi_u` payload with extra data of type `MemoryRip`. + mi_u_rip, + /// Memory (SIB), immediate (sign-extend) operands. + /// Uses `xi_s` payload with extra data of type `MemorySib`. + mi_s_sib, + /// Memory (RIP), immediate (sign-extend) operands. + /// Uses `xi_s` payload with extra data of type `MemoryRip`. + mi_s_rip, + /// Memory (SIB), register operands. + /// Uses `rx` payload with extra data of type `MemorySib`. + mr_sib, + /// Memory (RIP), register operands. + /// Uses `rx` payload with extra data of type `MemoryRip`. + mr_rip, + /// Rax, Memory moffs. + /// Uses `payload` with extra data of type `MemoryMoffs`. + rax_moffs, + /// Memory moffs, rax. + /// Uses `payload` with extra data of type `MemoryMoffs`. + moffs_rax, /// Lea into register with linker relocation. /// Uses `payload` payload with data of type `LeaRegisterReloc`. lea_r_reloc, @@ -274,6 +305,16 @@ pub const Inst = struct { r1: Register, payload: u32, }, + /// Custom payload followed by an unsigned immediate. + xi_u: struct { + payload: u32, + imm: u32, + }, + /// Custom payload followed by a signed immediate. + xi_s: struct { + payload: u32, + imm: i32, + }, /// Relocation for the linker where: /// * `atom_index` is the index of the source /// * `sym_index` is the index of the target @@ -378,6 +419,90 @@ pub const Imm64 = struct { } }; +// TODO this can be further compacted using packed struct +pub const MemorySib = struct { + /// Size of the pointer. + ptr_size: u32, + /// Base register. -1 means null, or no base register. + base: i32, + /// Scale for index register. -1 means null, or no scale. + /// This has to be in sync with `index` field. + scale: i32, + /// Index register. -1 means null, or no index register. + /// This has to be in sync with `scale` field. + index: i32, + /// Displacement value. + disp: i32, + + pub fn encode(mem: Memory) MemorySib { + const sib = mem.sib; + return .{ + .ptr_size = @enumToInt(sib.ptr_size), + .base = if (sib.base) |r| @enumToInt(r) else -1, + .scale = if (sib.scale_index) |si| si.scale else -1, + .index = if (sib.scale_index) |si| @enumToInt(si.index) else -1, + .disp = sib.disp, + }; + } + + pub fn decode(msib: MemorySib) Memory { + const base: ?Register = if (msib.base == -1) null else @intToEnum(Register, msib.base); + const scale_index: ?Memory.ScaleIndex = if (msib.index == -1) null else .{ + .scale = @intCast(u4, msib.scale), + .index = @intToEnum(Register, msib.index), + }; + const mem: Memory = .{ .sib = .{ + .ptr_size = @intToEnum(Memory.PtrSize, msib.ptr_size), + .base = base, + .scale_index = scale_index, + .disp = msib.disp, + } }; + return mem; + } +}; + +pub const MemoryRip = struct { + /// Size of the pointer. + ptr_size: u32, + /// Displacement value. + disp: i32, + + pub fn encode(mem: Memory) MemoryRip { + return .{ + .ptr_size = @enumToInt(mem.rip.ptr_size), + .disp = mem.rip.disp, + }; + } + + pub fn decode(mrip: MemoryRip) Memory { + return .{ .rip = .{ + .ptr_size = @intToEnum(Memory.PtrSize, mrip.ptr_size), + .disp = mrip.disp, + } }; + } +}; + +pub const MemoryMoffs = struct { + /// Segment register. + seg: u32, + /// Absolute offset wrt to the segment register split between MSB and LSB parts much like + /// `Imm64` payload. + msb: u32, + lsb: u32, + + pub fn encodeOffset(moffs: *MemoryMoffs, v: u64) void { + moffs.msb = @truncate(u32, v >> 32); + moffs.lsb = @truncate(u32, v); + } + + pub fn decodeOffset(moffs: *const MemoryMoffs) u64 { + var res: u64 = 0; + res |= (@intCast(u64, moffs.msb) << 32); + res |= @intCast(u64, moffs.lsb); + return res; + } +}; + pub const DbgLineColumn = struct { line: u32, column: u32, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index ad9a6f7f23..b6ac9ec5a8 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -417,6 +417,17 @@ pub const Memory = union(enum) { qword, tbyte, + pub fn fromSize(size: u32) PtrSize { + return switch (size) { + 1 => .byte, + 2 => .word, + 4 => .dword, + 8 => .qword, + 10 => .tbyte, + else => unreachable, + }; + } + pub fn fromBitSize(bit_size: u64) PtrSize { return switch (bit_size) { 8 => .byte, |
