mirror of
https://github.com/ziglang/zig.git
synced 2025-12-29 09:33:18 +00:00
x86_64: factor out lowering from emitting
This commit is contained in:
parent
0e5e001278
commit
abb37a7cb8
@ -341,19 +341,22 @@ pub fn generate(
|
||||
defer mir.deinit(bin_file.allocator);
|
||||
|
||||
var emit = Emit{
|
||||
.mir = mir,
|
||||
.lower = .{
|
||||
.allocator = bin_file.allocator,
|
||||
.mir = mir,
|
||||
.target = &bin_file.options.target,
|
||||
.src_loc = src_loc,
|
||||
},
|
||||
.bin_file = bin_file,
|
||||
.debug_output = debug_output,
|
||||
.target = &bin_file.options.target,
|
||||
.src_loc = src_loc,
|
||||
.code = code,
|
||||
.prev_di_pc = 0,
|
||||
.prev_di_line = module_fn.lbrace_line,
|
||||
.prev_di_column = module_fn.lbrace_column,
|
||||
};
|
||||
defer emit.deinit();
|
||||
emit.lowerMir() catch |err| switch (err) {
|
||||
error.EmitFail => return Result{ .fail = emit.err_msg.? },
|
||||
emit.emitMir() catch |err| switch (err) {
|
||||
error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? },
|
||||
error.InvalidInstruction, error.CannotEncode => |e| {
|
||||
const msg = switch (e) {
|
||||
error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
|
||||
@ -7070,16 +7073,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
if (reg.to64() == .rax) {
|
||||
// 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) },
|
||||
.data = .{ .payload = try self.addExtra(Mir.MemoryMoffs.encode(.ds, x)) },
|
||||
});
|
||||
} else {
|
||||
// Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
|
||||
|
||||
@ -1,40 +1,8 @@
|
||||
//! This file contains the functionality for lowering x86_64 MIR into
|
||||
//! machine code
|
||||
//! This file contains the functionality for emitting x86_64 MIR as machine code
|
||||
|
||||
const Emit = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const bits = @import("bits.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const encoder = @import("encoder.zig");
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.codegen);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
|
||||
const Air = @import("../../Air.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const CodeGen = @import("CodeGen.zig");
|
||||
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
const Encoder = bits.Encoder;
|
||||
const ErrorMsg = Module.ErrorMsg;
|
||||
const Immediate = bits.Immediate;
|
||||
const Instruction = encoder.Instruction;
|
||||
const MCValue = @import("CodeGen.zig").MCValue;
|
||||
const Memory = bits.Memory;
|
||||
const Mir = @import("Mir.zig");
|
||||
const Module = @import("../../Module.zig");
|
||||
const Register = bits.Register;
|
||||
const Type = @import("../../type.zig").Type;
|
||||
|
||||
mir: Mir,
|
||||
lower: Lower,
|
||||
bin_file: *link.File,
|
||||
debug_output: DebugInfoOutput,
|
||||
target: *const std.Target,
|
||||
err_msg: ?*ErrorMsg = null,
|
||||
src_loc: Module.SrcLoc,
|
||||
code: *std.ArrayList(u8),
|
||||
|
||||
prev_di_line: u32,
|
||||
@ -45,12 +13,163 @@ prev_di_pc: usize,
|
||||
code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
|
||||
relocs: std.ArrayListUnmanaged(Reloc) = .{},
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
EmitFail,
|
||||
InvalidInstruction,
|
||||
CannotEncode,
|
||||
};
|
||||
pub const Error = Lower.Error || error{EmitFail};
|
||||
|
||||
pub fn emitMir(emit: *Emit) Error!void {
|
||||
for (0..emit.lower.mir.instructions.len) |i| {
|
||||
const index = @intCast(Mir.Inst.Index, i);
|
||||
const inst = emit.lower.mir.instructions.get(index);
|
||||
|
||||
const start_offset = @intCast(u32, emit.code.items.len);
|
||||
try emit.code_offset_mapping.putNoClobber(emit.lower.allocator, index, start_offset);
|
||||
for (try emit.lower.lowerMir(inst)) |lower_inst| try lower_inst.encode(emit.code.writer());
|
||||
const end_offset = @intCast(u32, emit.code.items.len);
|
||||
|
||||
switch (inst.tag) {
|
||||
else => {},
|
||||
|
||||
.jmp_reloc => try emit.relocs.append(emit.lower.allocator, .{
|
||||
.source = start_offset,
|
||||
.target = inst.data.inst,
|
||||
.offset = end_offset - 4,
|
||||
.length = 5,
|
||||
}),
|
||||
|
||||
.call_extern => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(
|
||||
.{ .sym_index = inst.data.relocation.atom_index, .file = null },
|
||||
).?;
|
||||
const target = macho_file.getGlobalByIndex(inst.data.relocation.sym_index);
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
|
||||
.target = target,
|
||||
.offset = end_offset - 4,
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = coff_file.getAtomIndexForSymbol(
|
||||
.{ .sym_index = inst.data.relocation.atom_index, .file = null },
|
||||
).?;
|
||||
const target = coff_file.getGlobalByIndex(inst.data.relocation.sym_index);
|
||||
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
|
||||
.type = .direct,
|
||||
.target = target,
|
||||
.offset = end_offset - 4,
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
|
||||
|
||||
.lea_linker => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const metadata =
|
||||
emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
|
||||
const reloc_type = switch (inst.ops) {
|
||||
.got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
|
||||
.direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
|
||||
else => unreachable,
|
||||
};
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{
|
||||
.sym_index = metadata.atom_index,
|
||||
.file = null,
|
||||
}).?;
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = reloc_type,
|
||||
.target = .{ .sym_index = metadata.sym_index, .file = null },
|
||||
.offset = @intCast(u32, end_offset - 4),
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
const metadata =
|
||||
emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
|
||||
const atom_index = coff_file.getAtomIndexForSymbol(.{
|
||||
.sym_index = metadata.atom_index,
|
||||
.file = null,
|
||||
}).?;
|
||||
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
|
||||
.type = switch (inst.ops) {
|
||||
.got_reloc => .got,
|
||||
.direct_reloc => .direct,
|
||||
.import_reloc => .import,
|
||||
else => unreachable,
|
||||
},
|
||||
.target = switch (inst.ops) {
|
||||
.got_reloc,
|
||||
.direct_reloc,
|
||||
=> .{ .sym_index = metadata.sym_index, .file = null },
|
||||
.import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
|
||||
else => unreachable,
|
||||
},
|
||||
.offset = @intCast(u32, end_offset - 4),
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
|
||||
|
||||
.jcc => try emit.relocs.append(emit.lower.allocator, .{
|
||||
.source = start_offset,
|
||||
.target = inst.data.inst_cc.inst,
|
||||
.offset = end_offset - 4,
|
||||
.length = 6,
|
||||
}),
|
||||
|
||||
.dbg_line => {
|
||||
const dbg_line_column =
|
||||
emit.lower.mir.extraData(Mir.DbgLineColumn, inst.data.payload).data;
|
||||
try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
|
||||
},
|
||||
|
||||
.dbg_prologue_end => {
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
try dw.setPrologueEnd();
|
||||
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
|
||||
emit.prev_di_line, emit.prev_di_column,
|
||||
});
|
||||
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
},
|
||||
|
||||
.dbg_epilogue_begin => {
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
try dw.setEpilogueBegin();
|
||||
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
|
||||
emit.prev_di_line, emit.prev_di_column,
|
||||
});
|
||||
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
try emit.fixupRelocs();
|
||||
}
|
||||
|
||||
pub fn deinit(emit: *Emit) void {
|
||||
emit.relocs.deinit(emit.lower.allocator);
|
||||
emit.code_offset_mapping.deinit(emit.lower.allocator);
|
||||
emit.* = undefined;
|
||||
}
|
||||
|
||||
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
|
||||
return switch (emit.lower.fail(format, args)) {
|
||||
error.LowerFail => error.EmitFail,
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
|
||||
const Reloc = struct {
|
||||
/// Offset of the instruction.
|
||||
@ -63,148 +182,7 @@ const Reloc = struct {
|
||||
length: u5,
|
||||
};
|
||||
|
||||
pub fn lowerMir(emit: *Emit) InnerError!void {
|
||||
const mir_tags = emit.mir.instructions.items(.tag);
|
||||
|
||||
for (mir_tags, 0..) |tag, index| {
|
||||
const inst = @intCast(u32, index);
|
||||
try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
|
||||
switch (tag) {
|
||||
.adc,
|
||||
.add,
|
||||
.@"and",
|
||||
.bsf,
|
||||
.bsr,
|
||||
.bswap,
|
||||
.bt,
|
||||
.btc,
|
||||
.btr,
|
||||
.bts,
|
||||
.call,
|
||||
.cbw,
|
||||
.cwde,
|
||||
.cdqe,
|
||||
.cwd,
|
||||
.cdq,
|
||||
.cqo,
|
||||
.cmp,
|
||||
.cmpxchg,
|
||||
.div,
|
||||
.fisttp,
|
||||
.fld,
|
||||
.idiv,
|
||||
.imul,
|
||||
.int3,
|
||||
.jmp,
|
||||
.lea,
|
||||
.lfence,
|
||||
.lzcnt,
|
||||
.mfence,
|
||||
.mov,
|
||||
.movbe,
|
||||
.movzx,
|
||||
.mul,
|
||||
.neg,
|
||||
.nop,
|
||||
.not,
|
||||
.@"or",
|
||||
.pop,
|
||||
.popcnt,
|
||||
.push,
|
||||
.rcl,
|
||||
.rcr,
|
||||
.ret,
|
||||
.rol,
|
||||
.ror,
|
||||
.sal,
|
||||
.sar,
|
||||
.sbb,
|
||||
.sfence,
|
||||
.shl,
|
||||
.shld,
|
||||
.shr,
|
||||
.shrd,
|
||||
.sub,
|
||||
.syscall,
|
||||
.@"test",
|
||||
.tzcnt,
|
||||
.ud2,
|
||||
.xadd,
|
||||
.xchg,
|
||||
.xor,
|
||||
|
||||
.addss,
|
||||
.cmpss,
|
||||
.divss,
|
||||
.maxss,
|
||||
.minss,
|
||||
.movss,
|
||||
.mulss,
|
||||
.roundss,
|
||||
.subss,
|
||||
.ucomiss,
|
||||
.addsd,
|
||||
.cmpsd,
|
||||
.divsd,
|
||||
.maxsd,
|
||||
.minsd,
|
||||
.movsd,
|
||||
.mulsd,
|
||||
.roundsd,
|
||||
.subsd,
|
||||
.ucomisd,
|
||||
=> try emit.mirEncodeGeneric(tag, inst),
|
||||
|
||||
.cmps,
|
||||
.lods,
|
||||
.movs,
|
||||
.scas,
|
||||
.stos,
|
||||
=> try emit.mirString(tag, inst),
|
||||
|
||||
.cmpxchgb => try emit.mirCmpxchgBytes(inst),
|
||||
|
||||
.jmp_reloc => try emit.mirJmpReloc(inst),
|
||||
|
||||
.call_extern => try emit.mirCallExtern(inst),
|
||||
|
||||
.lea_linker => try emit.mirLeaLinker(inst),
|
||||
|
||||
.mov_moffs => try emit.mirMovMoffs(inst),
|
||||
|
||||
.movsx => try emit.mirMovsx(inst),
|
||||
.cmovcc => try emit.mirCmovcc(inst),
|
||||
.setcc => try emit.mirSetcc(inst),
|
||||
.jcc => try emit.mirJcc(inst),
|
||||
|
||||
.dbg_line => try emit.mirDbgLine(inst),
|
||||
.dbg_prologue_end => try emit.mirDbgPrologueEnd(inst),
|
||||
.dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst),
|
||||
|
||||
.push_regs => try emit.mirPushPopRegisterList(.push, inst),
|
||||
.pop_regs => try emit.mirPushPopRegisterList(.pop, inst),
|
||||
|
||||
.dead => {},
|
||||
}
|
||||
}
|
||||
|
||||
try emit.fixupRelocs();
|
||||
}
|
||||
|
||||
pub fn deinit(emit: *Emit) void {
|
||||
emit.relocs.deinit(emit.bin_file.allocator);
|
||||
emit.code_offset_mapping.deinit(emit.bin_file.allocator);
|
||||
emit.* = undefined;
|
||||
}
|
||||
|
||||
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
|
||||
@setCold(true);
|
||||
assert(emit.err_msg == null);
|
||||
emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
|
||||
return error.EmitFail;
|
||||
}
|
||||
|
||||
fn fixupRelocs(emit: *Emit) InnerError!void {
|
||||
fn fixupRelocs(emit: *Emit) Error!void {
|
||||
// TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.
|
||||
// This should be reversed like it is done in aarch64 MIR emit code: start with the smallest
|
||||
// possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution
|
||||
@ -217,532 +195,7 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: Instruction.Init) InnerError!void {
|
||||
const inst = try Instruction.new(mnemonic, ops);
|
||||
return inst.encode(emit.code.writer());
|
||||
}
|
||||
|
||||
fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
|
||||
const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| {
|
||||
if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name);
|
||||
} else unreachable;
|
||||
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
const data = emit.mir.instructions.items(.data)[inst];
|
||||
|
||||
const prefix: Instruction.Prefix = switch (ops) {
|
||||
.lock_m_sib,
|
||||
.lock_m_rip,
|
||||
.lock_mi_sib_u,
|
||||
.lock_mi_rip_u,
|
||||
.lock_mi_sib_s,
|
||||
.lock_mi_rip_s,
|
||||
.lock_mr_sib,
|
||||
.lock_mr_rip,
|
||||
.lock_moffs_rax,
|
||||
=> .lock,
|
||||
else => .none,
|
||||
};
|
||||
|
||||
var op1: Instruction.Operand = .none;
|
||||
var op2: Instruction.Operand = .none;
|
||||
var op3: Instruction.Operand = .none;
|
||||
var op4: Instruction.Operand = .none;
|
||||
|
||||
switch (ops) {
|
||||
.none => {},
|
||||
.i_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.i)) },
|
||||
.i_u => op1 = .{ .imm = Immediate.u(data.i) },
|
||||
.r => op1 = .{ .reg = data.r },
|
||||
.rr => {
|
||||
op1 = .{ .reg = data.rr.r1 };
|
||||
op2 = .{ .reg = data.rr.r2 };
|
||||
},
|
||||
.rrr => {
|
||||
op1 = .{ .reg = data.rrr.r1 };
|
||||
op2 = .{ .reg = data.rrr.r2 };
|
||||
op3 = .{ .reg = data.rrr.r3 };
|
||||
},
|
||||
.ri_s, .ri_u => {
|
||||
const imm = switch (ops) {
|
||||
.ri_s => Immediate.s(@bitCast(i32, data.ri.i)),
|
||||
.ri_u => Immediate.u(data.ri.i),
|
||||
else => unreachable,
|
||||
};
|
||||
op1 = .{ .reg = data.ri.r };
|
||||
op2 = .{ .imm = imm };
|
||||
},
|
||||
.ri64 => {
|
||||
const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data;
|
||||
op1 = .{ .reg = data.rx.r };
|
||||
op2 = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) };
|
||||
},
|
||||
.rri_s, .rri_u => {
|
||||
const imm = switch (ops) {
|
||||
.rri_s => Immediate.s(@bitCast(i32, data.rri.i)),
|
||||
.rri_u => Immediate.u(data.rri.i),
|
||||
else => unreachable,
|
||||
};
|
||||
op1 = .{ .reg = data.rri.r1 };
|
||||
op2 = .{ .reg = data.rri.r2 };
|
||||
op3 = .{ .imm = imm };
|
||||
},
|
||||
.m_sib, .lock_m_sib => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data;
|
||||
op1 = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
},
|
||||
.m_rip, .lock_m_rip => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
|
||||
op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
},
|
||||
.mi_sib_s, .mi_sib_u, .lock_mi_sib_s, .lock_mi_sib_u => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.ix.payload).data;
|
||||
const imm = switch (ops) {
|
||||
.mi_sib_s, .lock_mi_sib_s => Immediate.s(@bitCast(i32, data.ix.i)),
|
||||
.mi_sib_u, .lock_mi_sib_u => Immediate.u(data.ix.i),
|
||||
else => unreachable,
|
||||
};
|
||||
op1 = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
op2 = .{ .imm = imm };
|
||||
},
|
||||
.mi_rip_u, .mi_rip_s, .lock_mi_rip_u, .lock_mi_rip_s => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.ix.payload).data;
|
||||
const imm = switch (ops) {
|
||||
.mi_rip_s, .lock_mi_rip_s => Immediate.s(@bitCast(i32, data.ix.i)),
|
||||
.mi_rip_u, .lock_mi_rip_u => Immediate.u(data.ix.i),
|
||||
else => unreachable,
|
||||
};
|
||||
op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
op2 = .{ .imm = imm };
|
||||
},
|
||||
.rm_sib, .mr_sib, .lock_mr_sib => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data;
|
||||
const op_r = .{ .reg = data.rx.r };
|
||||
const op_m = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
switch (ops) {
|
||||
.rm_sib => {
|
||||
op1 = op_r;
|
||||
op2 = op_m;
|
||||
},
|
||||
.mr_sib, .lock_mr_sib => {
|
||||
op1 = op_m;
|
||||
op2 = op_r;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.rm_rip, .mr_rip, .lock_mr_rip => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data;
|
||||
const op_r = .{ .reg = data.rx.r };
|
||||
const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
switch (ops) {
|
||||
.rm_rip => {
|
||||
op1 = op_r;
|
||||
op2 = op_m;
|
||||
},
|
||||
.mr_rip, .lock_mr_rip => {
|
||||
op1 = op_m;
|
||||
op2 = op_r;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.mrr_sib => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.rrx.payload).data;
|
||||
op1 = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
op2 = .{ .reg = data.rrx.r1 };
|
||||
op2 = .{ .reg = data.rrx.r2 };
|
||||
},
|
||||
.mrr_rip => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.rrx.payload).data;
|
||||
op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
op2 = .{ .reg = data.rrx.r1 };
|
||||
op2 = .{ .reg = data.rrx.r2 };
|
||||
},
|
||||
.mri_sib => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.rix.payload).data;
|
||||
op1 = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
op2 = .{ .reg = data.rix.r };
|
||||
op3 = .{ .imm = Immediate.u(data.rix.i) };
|
||||
},
|
||||
.mri_rip => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.rix.payload).data;
|
||||
op1 = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
op2 = .{ .reg = data.rix.r };
|
||||
op3 = .{ .imm = Immediate.u(data.rix.i) };
|
||||
},
|
||||
else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{
|
||||
@tagName(mnemonic),
|
||||
@tagName(ops),
|
||||
}),
|
||||
}
|
||||
|
||||
return emit.encode(mnemonic, .{
|
||||
.prefix = prefix,
|
||||
.op1 = op1,
|
||||
.op2 = op2,
|
||||
.op3 = op3,
|
||||
.op4 = op4,
|
||||
});
|
||||
}
|
||||
|
||||
fn mirString(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
switch (ops) {
|
||||
.string => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].string;
|
||||
const mnemonic = switch (tag) {
|
||||
inline .cmps, .lods, .movs, .scas, .stos => |comptime_tag| switch (data.width) {
|
||||
inline else => |comptime_width| @field(
|
||||
Instruction.Mnemonic,
|
||||
@tagName(comptime_tag) ++ @tagName(comptime_width),
|
||||
),
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
return emit.encode(mnemonic, .{ .prefix = switch (data.repeat) {
|
||||
inline else => |comptime_repeat| @field(Instruction.Prefix, @tagName(comptime_repeat)),
|
||||
} });
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirCmpxchgBytes(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
const data = emit.mir.instructions.items(.data)[inst];
|
||||
|
||||
var op1: Instruction.Operand = .none;
|
||||
switch (ops) {
|
||||
.m_sib, .lock_m_sib => {
|
||||
const sib = emit.mir.extraData(Mir.MemorySib, data.payload).data;
|
||||
op1 = .{ .mem = Mir.MemorySib.decode(sib) };
|
||||
},
|
||||
.m_rip, .lock_m_rip => {
|
||||
const rip = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
|
||||
op1 = .{ .mem = Mir.MemoryRip.decode(rip) };
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const mnemonic: Instruction.Mnemonic = switch (op1.mem.bitSize()) {
|
||||
64 => .cmpxchg8b,
|
||||
128 => .cmpxchg16b,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
return emit.encode(mnemonic, .{
|
||||
.prefix = switch (ops) {
|
||||
.m_sib, .m_rip => .none,
|
||||
.lock_m_sib, .lock_m_rip => .lock,
|
||||
else => unreachable,
|
||||
},
|
||||
.op1 = op1,
|
||||
});
|
||||
}
|
||||
|
||||
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, .lock_moffs_rax => {
|
||||
try emit.encode(.mov, .{
|
||||
.prefix = switch (ops) {
|
||||
.moffs_rax => .none,
|
||||
.lock_moffs_rax => .lock,
|
||||
else => unreachable,
|
||||
},
|
||||
.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];
|
||||
|
||||
var op1: Instruction.Operand = .none;
|
||||
var op2: Instruction.Operand = .none;
|
||||
switch (ops) {
|
||||
.rr => {
|
||||
op1 = .{ .reg = data.rr.r1 };
|
||||
op2 = .{ .reg = data.rr.r2 };
|
||||
},
|
||||
.rm_sib => {
|
||||
const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data;
|
||||
op1 = .{ .reg = data.rx.r };
|
||||
op2 = .{ .mem = Mir.MemorySib.decode(msib) };
|
||||
},
|
||||
.rm_rip => {
|
||||
const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data;
|
||||
op1 = .{ .reg = data.rx.r };
|
||||
op2 = .{ .mem = Mir.MemoryRip.decode(mrip) };
|
||||
},
|
||||
else => unreachable, // TODO
|
||||
}
|
||||
|
||||
const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) {
|
||||
32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx,
|
||||
else => .movsx,
|
||||
};
|
||||
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = op1,
|
||||
.op2 = op2,
|
||||
});
|
||||
}
|
||||
|
||||
fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic {
|
||||
return switch (cc) {
|
||||
inline else => |comptime_cc| @field(Instruction.Mnemonic, basename ++ @tagName(comptime_cc)),
|
||||
};
|
||||
}
|
||||
|
||||
fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
switch (ops) {
|
||||
.rr_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].rr_cc;
|
||||
const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .reg = data.r1 },
|
||||
.op2 = .{ .reg = data.r2 },
|
||||
});
|
||||
},
|
||||
.rm_sib_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].rx_cc;
|
||||
const extra = emit.mir.extraData(Mir.MemorySib, data.payload).data;
|
||||
const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .reg = data.r },
|
||||
.op2 = .{ .mem = Mir.MemorySib.decode(extra) },
|
||||
});
|
||||
},
|
||||
.rm_rip_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].rx_cc;
|
||||
const extra = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
|
||||
const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .reg = data.r },
|
||||
.op2 = .{ .mem = Mir.MemoryRip.decode(extra) },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
switch (ops) {
|
||||
.r_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].r_cc;
|
||||
const mnemonic = mnemonicFromConditionCode("set", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .reg = data.r },
|
||||
});
|
||||
},
|
||||
.m_sib_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].x_cc;
|
||||
const extra = emit.mir.extraData(Mir.MemorySib, data.payload).data;
|
||||
const mnemonic = mnemonicFromConditionCode("set", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .mem = Mir.MemorySib.decode(extra) },
|
||||
});
|
||||
},
|
||||
.m_rip_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].x_cc;
|
||||
const extra = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
|
||||
const mnemonic = mnemonicFromConditionCode("set", data.cc);
|
||||
return emit.encode(mnemonic, .{
|
||||
.op1 = .{ .mem = Mir.MemoryRip.decode(extra) },
|
||||
});
|
||||
},
|
||||
else => unreachable, // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const ops = emit.mir.instructions.items(.ops)[inst];
|
||||
switch (ops) {
|
||||
.inst_cc => {
|
||||
const data = emit.mir.instructions.items(.data)[inst].inst_cc;
|
||||
const mnemonic = mnemonicFromConditionCode("j", data.cc);
|
||||
const source = emit.code.items.len;
|
||||
try emit.encode(mnemonic, .{
|
||||
.op1 = .{ .imm = Immediate.s(0) },
|
||||
});
|
||||
try emit.relocs.append(emit.bin_file.allocator, .{
|
||||
.source = source,
|
||||
.target = data.inst,
|
||||
.offset = emit.code.items.len - 4,
|
||||
.length = 6,
|
||||
});
|
||||
},
|
||||
else => unreachable, // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const target = emit.mir.instructions.items(.data)[inst].inst;
|
||||
const source = emit.code.items.len;
|
||||
try emit.encode(.jmp, .{
|
||||
.op1 = .{ .imm = Immediate.s(0) },
|
||||
});
|
||||
try emit.relocs.append(emit.bin_file.allocator, .{
|
||||
.source = source,
|
||||
.target = target,
|
||||
.offset = emit.code.items.len - 4,
|
||||
.length = 5,
|
||||
});
|
||||
}
|
||||
|
||||
fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const relocation = emit.mir.instructions.items(.data)[inst].relocation;
|
||||
|
||||
const offset = blk: {
|
||||
try emit.encode(.call, .{
|
||||
.op1 = .{ .imm = Immediate.s(0) },
|
||||
});
|
||||
break :blk @intCast(u32, emit.code.items.len) - 4;
|
||||
};
|
||||
|
||||
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
|
||||
const target = macho_file.getGlobalByIndex(relocation.sym_index);
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
|
||||
.target = target,
|
||||
.offset = offset,
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
|
||||
const target = coff_file.getGlobalByIndex(relocation.sym_index);
|
||||
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
|
||||
.type = .direct,
|
||||
.target = target,
|
||||
.offset = offset,
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else {
|
||||
return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{});
|
||||
}
|
||||
}
|
||||
|
||||
fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
|
||||
const payload = emit.mir.instructions.items(.data)[inst].payload;
|
||||
const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
|
||||
const base = @intToEnum(Register, save_reg_list.base_reg);
|
||||
var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
|
||||
const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
|
||||
for (callee_preserved_regs) |reg| {
|
||||
if (reg_list.isSet(callee_preserved_regs, reg)) {
|
||||
const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{
|
||||
.base = base,
|
||||
.disp = disp,
|
||||
}) };
|
||||
const op2: Instruction.Operand = .{ .reg = reg };
|
||||
switch (tag) {
|
||||
.push => try emit.encode(.mov, .{
|
||||
.op1 = op1,
|
||||
.op2 = op2,
|
||||
}),
|
||||
.pop => try emit.encode(.mov, .{
|
||||
.op1 = op2,
|
||||
.op2 = op1,
|
||||
}),
|
||||
else => unreachable,
|
||||
}
|
||||
disp += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mirLeaLinker(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 metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data;
|
||||
const reg = @intToEnum(Register, metadata.reg);
|
||||
|
||||
try emit.encode(.lea, .{
|
||||
.op1 = .{ .reg = reg },
|
||||
.op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
|
||||
});
|
||||
|
||||
const end_offset = emit.code.items.len;
|
||||
|
||||
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const reloc_type = switch (ops) {
|
||||
.got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
|
||||
.direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
|
||||
else => unreachable,
|
||||
};
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{
|
||||
.sym_index = metadata.atom_index,
|
||||
.file = null,
|
||||
}).?;
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = reloc_type,
|
||||
.target = .{ .sym_index = metadata.sym_index, .file = null },
|
||||
.offset = @intCast(u32, end_offset - 4),
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
const atom_index = coff_file.getAtomIndexForSymbol(.{
|
||||
.sym_index = metadata.atom_index,
|
||||
.file = null,
|
||||
}).?;
|
||||
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
|
||||
.type = switch (ops) {
|
||||
.got_reloc => .got,
|
||||
.direct_reloc => .direct,
|
||||
.import_reloc => .import,
|
||||
else => unreachable,
|
||||
},
|
||||
.target = switch (ops) {
|
||||
.got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null },
|
||||
.import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
|
||||
else => unreachable,
|
||||
},
|
||||
.offset = @intCast(u32, end_offset - 4),
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
.length = 2,
|
||||
});
|
||||
} else {
|
||||
return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{});
|
||||
}
|
||||
}
|
||||
|
||||
fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const payload = emit.mir.instructions.items(.data)[inst].payload;
|
||||
const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data;
|
||||
log.debug("mirDbgLine", .{});
|
||||
try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
|
||||
}
|
||||
|
||||
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
|
||||
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
|
||||
const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
|
||||
const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
|
||||
log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
|
||||
@ -756,7 +209,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.target.cpu.arch) catch unreachable;
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.lower.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
@ -792,34 +245,12 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
_ = inst;
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
try dw.setPrologueEnd();
|
||||
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
|
||||
emit.prev_di_line,
|
||||
emit.prev_di_column,
|
||||
});
|
||||
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.emit);
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
|
||||
fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
_ = inst;
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
try dw.setEpilogueBegin();
|
||||
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
|
||||
emit.prev_di_line,
|
||||
emit.prev_di_column,
|
||||
});
|
||||
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
const Emit = @This();
|
||||
const Lower = @import("Lower.zig");
|
||||
const Mir = @import("Mir.zig");
|
||||
|
||||
@ -7,30 +7,32 @@ const math = std.math;
|
||||
const bits = @import("bits.zig");
|
||||
const encoder = @import("encoder.zig");
|
||||
const Instruction = encoder.Instruction;
|
||||
const Operand = Instruction.Operand;
|
||||
const Prefix = Instruction.Prefix;
|
||||
const Register = bits.Register;
|
||||
const Rex = encoder.Rex;
|
||||
const LegacyPrefixes = encoder.LegacyPrefixes;
|
||||
|
||||
const table = @import("encodings.zig").table;
|
||||
|
||||
mnemonic: Mnemonic,
|
||||
op_en: OpEn,
|
||||
op1: Op,
|
||||
op2: Op,
|
||||
op3: Op,
|
||||
op4: Op,
|
||||
opc_len: u3,
|
||||
opc: [7]u8,
|
||||
modrm_ext: u3,
|
||||
mode: Mode,
|
||||
data: Data,
|
||||
|
||||
pub fn findByMnemonic(mnemonic: Mnemonic, args: Instruction.Init) !?Encoding {
|
||||
const input_op1 = Op.fromOperand(args.op1);
|
||||
const input_op2 = Op.fromOperand(args.op2);
|
||||
const input_op3 = Op.fromOperand(args.op3);
|
||||
const input_op4 = Op.fromOperand(args.op4);
|
||||
const Data = struct {
|
||||
op_en: OpEn,
|
||||
ops: [4]Op,
|
||||
opc_len: u3,
|
||||
opc: [7]u8,
|
||||
modrm_ext: u3,
|
||||
mode: Mode,
|
||||
};
|
||||
|
||||
pub fn findByMnemonic(
|
||||
prefix: Instruction.Prefix,
|
||||
mnemonic: Mnemonic,
|
||||
ops: []const Instruction.Operand,
|
||||
) !?Encoding {
|
||||
var input_ops = [1]Op{.none} ** 4;
|
||||
for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op);
|
||||
|
||||
const ops = &[_]Instruction.Operand{ args.op1, args.op2, args.op3, args.op4 };
|
||||
const rex_required = for (ops) |op| switch (op) {
|
||||
.reg => |r| switch (r) {
|
||||
.spl, .bpl, .sil, .dil => break true,
|
||||
@ -60,88 +62,29 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: Instruction.Init) !?Encoding {
|
||||
|
||||
if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode;
|
||||
|
||||
// TODO work out what is the maximum number of variants we can actually find in one swoop.
|
||||
var candidates: [10]Encoding = undefined;
|
||||
var count: usize = 0;
|
||||
for (table) |entry| {
|
||||
var enc = Encoding{
|
||||
.mnemonic = entry[0],
|
||||
.op_en = entry[1],
|
||||
.op1 = entry[2],
|
||||
.op2 = entry[3],
|
||||
.op3 = entry[4],
|
||||
.op4 = entry[5],
|
||||
.opc_len = @intCast(u3, entry[6].len),
|
||||
.opc = undefined,
|
||||
.modrm_ext = entry[7],
|
||||
.mode = entry[8],
|
||||
};
|
||||
std.mem.copy(u8, &enc.opc, entry[6]);
|
||||
if (enc.mnemonic == mnemonic and
|
||||
input_op1.isSubset(enc.op1, enc.mode) and
|
||||
input_op2.isSubset(enc.op2, enc.mode) and
|
||||
input_op3.isSubset(enc.op3, enc.mode) and
|
||||
input_op4.isSubset(enc.op4, enc.mode))
|
||||
{
|
||||
if (rex_required) {
|
||||
switch (enc.mode) {
|
||||
.rex, .long => {
|
||||
candidates[count] = enc;
|
||||
count += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
} else {
|
||||
if (enc.mode != .rex) {
|
||||
candidates[count] = enc;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
var shortest_enc: ?Encoding = null;
|
||||
var shortest_len: ?usize = null;
|
||||
next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| {
|
||||
switch (data.mode) {
|
||||
.rex => if (!rex_required) continue,
|
||||
.long => {},
|
||||
else => if (rex_required) continue,
|
||||
}
|
||||
for (input_ops, data.ops) |input_op, data_op|
|
||||
if (!input_op.isSubset(data_op, data.mode)) continue :next;
|
||||
|
||||
const enc = Encoding{ .mnemonic = mnemonic, .data = data };
|
||||
if (shortest_enc) |previous_shortest_enc| {
|
||||
const len = estimateInstructionLength(prefix, enc, ops);
|
||||
const previous_shortest_len = shortest_len orelse
|
||||
estimateInstructionLength(prefix, previous_shortest_enc, ops);
|
||||
if (len < previous_shortest_len) {
|
||||
shortest_enc = enc;
|
||||
shortest_len = len;
|
||||
} else shortest_len = previous_shortest_len;
|
||||
} else shortest_enc = enc;
|
||||
}
|
||||
|
||||
if (count == 0) return null;
|
||||
if (count == 1) return candidates[0];
|
||||
|
||||
const EncodingLength = struct {
|
||||
fn estimate(encoding: Encoding, params: Instruction.Init) usize {
|
||||
var inst = Instruction{
|
||||
.op1 = params.op1,
|
||||
.op2 = params.op2,
|
||||
.op3 = params.op3,
|
||||
.op4 = params.op4,
|
||||
.prefix = params.prefix,
|
||||
.encoding = encoding,
|
||||
};
|
||||
var cwriter = std.io.countingWriter(std.io.null_writer);
|
||||
inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM.
|
||||
return @intCast(usize, cwriter.bytes_written);
|
||||
}
|
||||
};
|
||||
|
||||
var shortest_encoding: ?struct {
|
||||
index: usize,
|
||||
len: usize,
|
||||
} = null;
|
||||
var i: usize = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
const candidate = candidates[i];
|
||||
switch (candidate.mode) {
|
||||
.long, .rex => if (rex_invalid) return error.CannotEncode,
|
||||
else => {},
|
||||
}
|
||||
|
||||
const len = EncodingLength.estimate(candidate, args);
|
||||
const current = shortest_encoding orelse {
|
||||
shortest_encoding = .{ .index = i, .len = len };
|
||||
continue;
|
||||
};
|
||||
if (len < current.len) {
|
||||
shortest_encoding = .{ .index = i, .len = len };
|
||||
}
|
||||
}
|
||||
|
||||
return candidates[shortest_encoding.?.index];
|
||||
return shortest_enc;
|
||||
}
|
||||
|
||||
/// Returns first matching encoding by opcode.
|
||||
@ -149,57 +92,45 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct {
|
||||
legacy: LegacyPrefixes,
|
||||
rex: Rex,
|
||||
}, modrm_ext: ?u3) ?Encoding {
|
||||
for (table) |entry| {
|
||||
const enc = Encoding{
|
||||
.mnemonic = entry[0],
|
||||
.op_en = entry[1],
|
||||
.op1 = entry[2],
|
||||
.op2 = entry[3],
|
||||
.op3 = entry[4],
|
||||
.op4 = entry[5],
|
||||
.opc_len = entry[6],
|
||||
.opc = .{ entry[7], entry[8], entry[9] },
|
||||
.modrm_ext = entry[10],
|
||||
.mode = entry[11],
|
||||
};
|
||||
const match = match: {
|
||||
if (modrm_ext) |ext| {
|
||||
break :match ext == enc.modrm_ext and std.mem.eql(u8, enc.opcode(), opc);
|
||||
for (mnemonic_to_encodings_map, 0..) |encs, mnemonic_int| for (encs) |data| {
|
||||
const enc = Encoding{ .mnemonic = @intToEnum(Mnemonic, mnemonic_int), .data = data };
|
||||
if (modrm_ext) |ext| if (ext != data.modrm_ext) continue;
|
||||
if (!std.mem.eql(u8, opc, enc.opcode())) continue;
|
||||
if (prefixes.rex.w) {
|
||||
switch (data.mode) {
|
||||
.short, .fpu, .sse, .sse2, .sse4_1, .none => continue,
|
||||
.long, .rex => {},
|
||||
}
|
||||
break :match std.mem.eql(u8, enc.opcode(), opc);
|
||||
};
|
||||
if (match) {
|
||||
if (prefixes.rex.w) {
|
||||
switch (enc.mode) {
|
||||
.fpu, .sse, .sse2, .sse4_1, .none => {},
|
||||
.long, .rex => return enc,
|
||||
}
|
||||
} else if (prefixes.rex.present and !prefixes.rex.isSet()) {
|
||||
if (enc.mode == .rex) return enc;
|
||||
} else if (prefixes.legacy.prefix_66) {
|
||||
switch (enc.operandBitSize()) {
|
||||
16 => return enc,
|
||||
} else if (prefixes.rex.present and !prefixes.rex.isSet()) {
|
||||
switch (data.mode) {
|
||||
.rex => {},
|
||||
else => continue,
|
||||
}
|
||||
} else if (prefixes.legacy.prefix_66) {
|
||||
switch (enc.operandBitSize()) {
|
||||
16 => {},
|
||||
else => continue,
|
||||
}
|
||||
} else {
|
||||
switch (data.mode) {
|
||||
.none => switch (enc.operandBitSize()) {
|
||||
16 => continue,
|
||||
else => {},
|
||||
}
|
||||
} else {
|
||||
if (enc.mode == .none) {
|
||||
switch (enc.operandBitSize()) {
|
||||
16 => {},
|
||||
else => return enc,
|
||||
}
|
||||
}
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
return enc;
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn opcode(encoding: *const Encoding) []const u8 {
|
||||
return encoding.opc[0..encoding.opc_len];
|
||||
return encoding.data.opc[0..encoding.data.opc_len];
|
||||
}
|
||||
|
||||
pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
|
||||
const prefix = encoding.opc[0];
|
||||
const prefix = encoding.data.opc[0];
|
||||
return switch (prefix) {
|
||||
0x66, 0xf2, 0xf3 => prefix,
|
||||
else => null,
|
||||
@ -207,27 +138,27 @@ pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
|
||||
}
|
||||
|
||||
pub fn modRmExt(encoding: Encoding) u3 {
|
||||
return switch (encoding.op_en) {
|
||||
.m, .mi, .m1, .mc => encoding.modrm_ext,
|
||||
return switch (encoding.data.op_en) {
|
||||
.m, .mi, .m1, .mc => encoding.data.modrm_ext,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn operandBitSize(encoding: Encoding) u64 {
|
||||
switch (encoding.mode) {
|
||||
switch (encoding.data.mode) {
|
||||
.short => return 16,
|
||||
.long => return 64,
|
||||
else => {},
|
||||
}
|
||||
const bit_size: u64 = switch (encoding.op_en) {
|
||||
.np => switch (encoding.op1) {
|
||||
const bit_size: u64 = switch (encoding.data.op_en) {
|
||||
.np => switch (encoding.data.ops[0]) {
|
||||
.o16 => 16,
|
||||
.o32 => 32,
|
||||
.o64 => 64,
|
||||
else => 32,
|
||||
},
|
||||
.td => encoding.op2.bitSize(),
|
||||
else => encoding.op1.bitSize(),
|
||||
.td => encoding.data.ops[1].bitSize(),
|
||||
else => encoding.data.ops[0].bitSize(),
|
||||
};
|
||||
return bit_size;
|
||||
}
|
||||
@ -240,7 +171,7 @@ pub fn format(
|
||||
) !void {
|
||||
_ = options;
|
||||
_ = fmt;
|
||||
switch (encoding.mode) {
|
||||
switch (encoding.data.mode) {
|
||||
.long => try writer.writeAll("REX.W + "),
|
||||
else => {},
|
||||
}
|
||||
@ -249,10 +180,10 @@ pub fn format(
|
||||
try writer.print("{x:0>2} ", .{byte});
|
||||
}
|
||||
|
||||
switch (encoding.op_en) {
|
||||
switch (encoding.data.op_en) {
|
||||
.np, .fd, .td, .i, .zi, .d => {},
|
||||
.o, .oi => {
|
||||
const tag = switch (encoding.op1) {
|
||||
const tag = switch (encoding.data.ops[0]) {
|
||||
.r8 => "rb",
|
||||
.r16 => "rw",
|
||||
.r32 => "rd",
|
||||
@ -265,12 +196,12 @@ pub fn format(
|
||||
.mr, .rm, .rmi, .mri, .mrc => try writer.writeAll("/r "),
|
||||
}
|
||||
|
||||
switch (encoding.op_en) {
|
||||
switch (encoding.data.op_en) {
|
||||
.i, .d, .zi, .oi, .mi, .rmi, .mri => {
|
||||
const op = switch (encoding.op_en) {
|
||||
.i, .d => encoding.op1,
|
||||
.zi, .oi, .mi => encoding.op2,
|
||||
.rmi, .mri => encoding.op3,
|
||||
const op = switch (encoding.data.op_en) {
|
||||
.i, .d => encoding.data.ops[0],
|
||||
.zi, .oi, .mi => encoding.data.ops[1],
|
||||
.rmi, .mri => encoding.data.ops[2],
|
||||
else => unreachable,
|
||||
};
|
||||
const tag = switch (op) {
|
||||
@ -290,13 +221,12 @@ pub fn format(
|
||||
|
||||
try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
|
||||
|
||||
const ops = &[_]Op{ encoding.op1, encoding.op2, encoding.op3, encoding.op4 };
|
||||
for (ops) |op| switch (op) {
|
||||
for (encoding.data.ops) |op| switch (op) {
|
||||
.none, .o16, .o32, .o64 => break,
|
||||
else => try writer.print("{s} ", .{@tagName(op)}),
|
||||
};
|
||||
|
||||
const op_en = switch (encoding.op_en) {
|
||||
const op_en = switch (encoding.data.op_en) {
|
||||
.zi => .i,
|
||||
else => |op_en| op_en,
|
||||
};
|
||||
@ -604,3 +534,53 @@ pub const Mode = enum {
|
||||
sse2,
|
||||
sse4_1,
|
||||
};
|
||||
|
||||
fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize {
|
||||
var inst = Instruction{
|
||||
.prefix = prefix,
|
||||
.encoding = encoding,
|
||||
.ops = [1]Operand{.none} ** 4,
|
||||
};
|
||||
std.mem.copy(Operand, &inst.ops, ops);
|
||||
|
||||
var cwriter = std.io.countingWriter(std.io.null_writer);
|
||||
inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM.
|
||||
return @intCast(usize, cwriter.bytes_written);
|
||||
}
|
||||
|
||||
const mnemonic_to_encodings_map = init: {
|
||||
@setEvalBranchQuota(100_000);
|
||||
const encodings = @import("encodings.zig");
|
||||
var entries = encodings.table;
|
||||
std.sort.sort(encodings.Entry, &entries, {}, struct {
|
||||
fn lessThan(_: void, lhs: encodings.Entry, rhs: encodings.Entry) bool {
|
||||
return @enumToInt(lhs[0]) < @enumToInt(rhs[0]);
|
||||
}
|
||||
}.lessThan);
|
||||
var data_storage: [entries.len]Data = undefined;
|
||||
var mnemonic_map: [@typeInfo(Mnemonic).Enum.fields.len][]const Data = undefined;
|
||||
var mnemonic_int = 0;
|
||||
var mnemonic_start = 0;
|
||||
for (&data_storage, entries, 0..) |*data, entry, data_index| {
|
||||
data.* = .{
|
||||
.op_en = entry[1],
|
||||
.ops = undefined,
|
||||
.opc_len = entry[3].len,
|
||||
.opc = undefined,
|
||||
.modrm_ext = entry[4],
|
||||
.mode = entry[5],
|
||||
};
|
||||
std.mem.copy(Op, &data.ops, entry[2]);
|
||||
std.mem.copy(u8, &data.opc, entry[3]);
|
||||
|
||||
while (mnemonic_int < @enumToInt(entry[0])) : (mnemonic_int += 1) {
|
||||
mnemonic_map[mnemonic_int] = data_storage[mnemonic_start..data_index];
|
||||
mnemonic_start = data_index;
|
||||
}
|
||||
}
|
||||
while (mnemonic_int < mnemonic_map.len) : (mnemonic_int += 1) {
|
||||
mnemonic_map[mnemonic_int] = data_storage[mnemonic_start..];
|
||||
mnemonic_start = data_storage.len;
|
||||
}
|
||||
break :init mnemonic_map;
|
||||
};
|
||||
|
||||
465
src/arch/x86_64/Lower.zig
Normal file
465
src/arch/x86_64/Lower.zig
Normal file
@ -0,0 +1,465 @@
|
||||
//! This file contains the functionality for lowering x86_64 MIR to Instructions
|
||||
|
||||
allocator: Allocator,
|
||||
mir: Mir,
|
||||
target: *const std.Target,
|
||||
err_msg: ?*ErrorMsg = null,
|
||||
src_loc: Module.SrcLoc,
|
||||
result: [
|
||||
std.mem.max(usize, &.{
|
||||
abi.Win64.callee_preserved_regs.len,
|
||||
abi.SysV.callee_preserved_regs.len,
|
||||
})
|
||||
]Instruction = undefined,
|
||||
result_len: usize = undefined,
|
||||
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
LowerFail,
|
||||
InvalidInstruction,
|
||||
CannotEncode,
|
||||
};
|
||||
|
||||
/// The returned slice is overwritten by the next call to lowerMir.
|
||||
pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
|
||||
lower.result = undefined;
|
||||
errdefer lower.result = undefined;
|
||||
lower.result_len = 0;
|
||||
defer lower.result_len = undefined;
|
||||
|
||||
switch (inst.tag) {
|
||||
.adc,
|
||||
.add,
|
||||
.@"and",
|
||||
.bsf,
|
||||
.bsr,
|
||||
.bswap,
|
||||
.bt,
|
||||
.btc,
|
||||
.btr,
|
||||
.bts,
|
||||
.call,
|
||||
.cbw,
|
||||
.cwde,
|
||||
.cdqe,
|
||||
.cwd,
|
||||
.cdq,
|
||||
.cqo,
|
||||
.cmp,
|
||||
.cmpxchg,
|
||||
.div,
|
||||
.fisttp,
|
||||
.fld,
|
||||
.idiv,
|
||||
.imul,
|
||||
.int3,
|
||||
.jmp,
|
||||
.lea,
|
||||
.lfence,
|
||||
.lzcnt,
|
||||
.mfence,
|
||||
.mov,
|
||||
.movbe,
|
||||
.movzx,
|
||||
.mul,
|
||||
.neg,
|
||||
.nop,
|
||||
.not,
|
||||
.@"or",
|
||||
.pop,
|
||||
.popcnt,
|
||||
.push,
|
||||
.rcl,
|
||||
.rcr,
|
||||
.ret,
|
||||
.rol,
|
||||
.ror,
|
||||
.sal,
|
||||
.sar,
|
||||
.sbb,
|
||||
.sfence,
|
||||
.shl,
|
||||
.shld,
|
||||
.shr,
|
||||
.shrd,
|
||||
.sub,
|
||||
.syscall,
|
||||
.@"test",
|
||||
.tzcnt,
|
||||
.ud2,
|
||||
.xadd,
|
||||
.xchg,
|
||||
.xor,
|
||||
|
||||
.addss,
|
||||
.cmpss,
|
||||
.divss,
|
||||
.maxss,
|
||||
.minss,
|
||||
.movss,
|
||||
.mulss,
|
||||
.roundss,
|
||||
.subss,
|
||||
.ucomiss,
|
||||
.addsd,
|
||||
.cmpsd,
|
||||
.divsd,
|
||||
.maxsd,
|
||||
.minsd,
|
||||
.movsd,
|
||||
.mulsd,
|
||||
.roundsd,
|
||||
.subsd,
|
||||
.ucomisd,
|
||||
=> try lower.mirGeneric(inst),
|
||||
|
||||
.cmps,
|
||||
.lods,
|
||||
.movs,
|
||||
.scas,
|
||||
.stos,
|
||||
=> try lower.mirString(inst),
|
||||
|
||||
.cmpxchgb => try lower.mirCmpxchgBytes(inst),
|
||||
|
||||
.jmp_reloc => try lower.emit(.none, .jmp, &.{.{ .imm = Immediate.s(0) }}),
|
||||
|
||||
.call_extern => try lower.emit(.none, .call, &.{.{ .imm = Immediate.s(0) }}),
|
||||
|
||||
.lea_linker => try lower.mirLeaLinker(inst),
|
||||
|
||||
.mov_moffs => try lower.mirMovMoffs(inst),
|
||||
|
||||
.movsx => try lower.mirMovsx(inst),
|
||||
.cmovcc => try lower.mirCmovcc(inst),
|
||||
.setcc => try lower.mirSetcc(inst),
|
||||
.jcc => try lower.emit(.none, mnem_cc(.j, inst.data.inst_cc.cc), &.{.{ .imm = Immediate.s(0) }}),
|
||||
|
||||
.push_regs, .pop_regs => try lower.mirPushPopRegisterList(inst),
|
||||
|
||||
.dbg_line,
|
||||
.dbg_prologue_end,
|
||||
.dbg_epilogue_begin,
|
||||
.dead,
|
||||
=> {},
|
||||
}
|
||||
|
||||
return lower.result[0..lower.result_len];
|
||||
}
|
||||
|
||||
pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
|
||||
@setCold(true);
|
||||
assert(lower.err_msg == null);
|
||||
lower.err_msg = try ErrorMsg.create(lower.allocator, lower.src_loc, format, args);
|
||||
return error.LowerFail;
|
||||
}
|
||||
|
||||
fn mnem_cc(comptime base: @Type(.EnumLiteral), cc: bits.Condition) Mnemonic {
|
||||
return switch (cc) {
|
||||
inline else => |c| @field(Mnemonic, @tagName(base) ++ @tagName(c)),
|
||||
};
|
||||
}
|
||||
|
||||
fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
|
||||
return switch (ops) {
|
||||
.rri_s,
|
||||
.ri_s,
|
||||
.i_s,
|
||||
.mi_sib_s,
|
||||
.mi_rip_s,
|
||||
.lock_mi_sib_s,
|
||||
.lock_mi_rip_s,
|
||||
=> Immediate.s(@bitCast(i32, i)),
|
||||
|
||||
.rri_u,
|
||||
.ri_u,
|
||||
.i_u,
|
||||
.mi_sib_u,
|
||||
.mi_rip_u,
|
||||
.lock_mi_sib_u,
|
||||
.lock_mi_rip_u,
|
||||
.mri_sib,
|
||||
.mri_rip,
|
||||
=> Immediate.u(i),
|
||||
|
||||
.ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
|
||||
return switch (ops) {
|
||||
.rm_sib,
|
||||
.rm_sib_cc,
|
||||
.m_sib,
|
||||
.m_sib_cc,
|
||||
.mi_sib_u,
|
||||
.mi_sib_s,
|
||||
.mr_sib,
|
||||
.mrr_sib,
|
||||
.mri_sib,
|
||||
.lock_m_sib,
|
||||
.lock_mi_sib_u,
|
||||
.lock_mi_sib_s,
|
||||
.lock_mr_sib,
|
||||
=> lower.mir.extraData(Mir.MemorySib, payload).data.decode(),
|
||||
|
||||
.rm_rip,
|
||||
.rm_rip_cc,
|
||||
.m_rip,
|
||||
.m_rip_cc,
|
||||
.mi_rip_u,
|
||||
.mi_rip_s,
|
||||
.mr_rip,
|
||||
.mrr_rip,
|
||||
.mri_rip,
|
||||
.lock_m_rip,
|
||||
.lock_mi_rip_u,
|
||||
.lock_mi_rip_s,
|
||||
.lock_mr_rip,
|
||||
=> lower.mir.extraData(Mir.MemoryRip, payload).data.decode(),
|
||||
|
||||
.rax_moffs,
|
||||
.moffs_rax,
|
||||
.lock_moffs_rax,
|
||||
=> lower.mir.extraData(Mir.MemoryMoffs, payload).data.decode(),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
|
||||
lower.result[lower.result_len] = try Instruction.new(prefix, mnemonic, ops);
|
||||
lower.result_len += 1;
|
||||
}
|
||||
|
||||
fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
try lower.emit(switch (inst.ops) {
|
||||
else => .none,
|
||||
.lock_m_sib,
|
||||
.lock_m_rip,
|
||||
.lock_mi_sib_u,
|
||||
.lock_mi_rip_u,
|
||||
.lock_mi_sib_s,
|
||||
.lock_mi_rip_s,
|
||||
.lock_mr_sib,
|
||||
.lock_mr_rip,
|
||||
.lock_moffs_rax,
|
||||
=> .lock,
|
||||
}, switch (inst.tag) {
|
||||
inline else => |tag| if (@hasField(Mnemonic, @tagName(tag)))
|
||||
@field(Mnemonic, @tagName(tag))
|
||||
else
|
||||
unreachable,
|
||||
}, switch (inst.ops) {
|
||||
.none => &.{},
|
||||
.i_s, .i_u => &.{
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.i) },
|
||||
},
|
||||
.r => &.{
|
||||
.{ .reg = inst.data.r },
|
||||
},
|
||||
.rr => &.{
|
||||
.{ .reg = inst.data.rr.r1 },
|
||||
.{ .reg = inst.data.rr.r2 },
|
||||
},
|
||||
.rrr => &.{
|
||||
.{ .reg = inst.data.rrr.r1 },
|
||||
.{ .reg = inst.data.rrr.r2 },
|
||||
.{ .reg = inst.data.rrr.r3 },
|
||||
},
|
||||
.ri_s, .ri_u => &.{
|
||||
.{ .reg = inst.data.ri.r },
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.ri.i) },
|
||||
},
|
||||
.ri64 => &.{
|
||||
.{ .reg = inst.data.rx.r },
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.rx.payload) },
|
||||
},
|
||||
.rri_s, .rri_u => &.{
|
||||
.{ .reg = inst.data.rri.r1 },
|
||||
.{ .reg = inst.data.rri.r2 },
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.rri.i) },
|
||||
},
|
||||
.m_sib, .lock_m_sib, .m_rip, .lock_m_rip => &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
|
||||
},
|
||||
.mi_sib_s,
|
||||
.lock_mi_sib_s,
|
||||
.mi_sib_u,
|
||||
.lock_mi_sib_u,
|
||||
.mi_rip_u,
|
||||
.lock_mi_rip_u,
|
||||
.mi_rip_s,
|
||||
.lock_mi_rip_s,
|
||||
=> &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.ix.payload) },
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.ix.i) },
|
||||
},
|
||||
.rm_sib, .rm_rip => &.{
|
||||
.{ .reg = inst.data.rx.r },
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
|
||||
},
|
||||
.mr_sib, .lock_mr_sib, .mr_rip, .lock_mr_rip => &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
|
||||
.{ .reg = inst.data.rx.r },
|
||||
},
|
||||
.mrr_sib, .mrr_rip => &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
|
||||
.{ .reg = inst.data.rrx.r1 },
|
||||
.{ .reg = inst.data.rrx.r2 },
|
||||
},
|
||||
.mri_sib, .mri_rip => &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) },
|
||||
.{ .reg = inst.data.rix.r },
|
||||
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
|
||||
},
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
});
|
||||
}
|
||||
|
||||
fn mirString(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
switch (inst.ops) {
|
||||
.string => try lower.emit(switch (inst.data.string.repeat) {
|
||||
inline else => |repeat| @field(Prefix, @tagName(repeat)),
|
||||
}, switch (inst.tag) {
|
||||
inline .cmps, .lods, .movs, .scas, .stos => |tag| switch (inst.data.string.width) {
|
||||
inline else => |width| @field(Mnemonic, @tagName(tag) ++ @tagName(width)),
|
||||
},
|
||||
else => unreachable,
|
||||
}, &.{}),
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
}
|
||||
}
|
||||
|
||||
fn mirCmpxchgBytes(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
const ops: [1]Operand = switch (inst.ops) {
|
||||
.m_sib, .lock_m_sib, .m_rip, .lock_m_rip => .{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
|
||||
},
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
};
|
||||
try lower.emit(switch (inst.ops) {
|
||||
.m_sib, .m_rip => .none,
|
||||
.lock_m_sib, .lock_m_rip => .lock,
|
||||
else => unreachable,
|
||||
}, switch (@divExact(ops[0].bitSize(), 8)) {
|
||||
8 => .cmpxchg8b,
|
||||
16 => .cmpxchg16b,
|
||||
else => return lower.fail("invalid operand for {s}", .{@tagName(inst.tag)}),
|
||||
}, &ops);
|
||||
}
|
||||
|
||||
fn mirMovMoffs(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
try lower.emit(switch (inst.ops) {
|
||||
.rax_moffs, .moffs_rax => .none,
|
||||
.lock_moffs_rax => .lock,
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
}, .mov, switch (inst.ops) {
|
||||
.rax_moffs => &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
|
||||
},
|
||||
.moffs_rax, .lock_moffs_rax => &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
|
||||
.{ .reg = .rax },
|
||||
},
|
||||
else => unreachable,
|
||||
});
|
||||
}
|
||||
|
||||
fn mirMovsx(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
const ops: [2]Operand = switch (inst.ops) {
|
||||
.rr => .{
|
||||
.{ .reg = inst.data.rr.r1 },
|
||||
.{ .reg = inst.data.rr.r2 },
|
||||
},
|
||||
.rm_sib, .rm_rip => .{
|
||||
.{ .reg = inst.data.rx.r },
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
|
||||
},
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
};
|
||||
try lower.emit(.none, switch (ops[0].bitSize()) {
|
||||
32, 64 => switch (ops[1].bitSize()) {
|
||||
32 => .movsxd,
|
||||
else => .movsx,
|
||||
},
|
||||
else => .movsx,
|
||||
}, &ops);
|
||||
}
|
||||
|
||||
fn mirCmovcc(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
switch (inst.ops) {
|
||||
.rr_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rr_cc.cc), &.{
|
||||
.{ .reg = inst.data.rr_cc.r1 },
|
||||
.{ .reg = inst.data.rr_cc.r2 },
|
||||
}),
|
||||
.rm_sib_cc, .rm_rip_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rx_cc.cc), &.{
|
||||
.{ .reg = inst.data.rx_cc.r },
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.rx_cc.payload) },
|
||||
}),
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
}
|
||||
}
|
||||
|
||||
fn mirSetcc(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
switch (inst.ops) {
|
||||
.r_cc => try lower.emit(.none, mnem_cc(.set, inst.data.r_cc.cc), &.{
|
||||
.{ .reg = inst.data.r_cc.r },
|
||||
}),
|
||||
.m_sib_cc, .m_rip_cc => try lower.emit(.none, mnem_cc(.set, inst.data.x_cc.cc), &.{
|
||||
.{ .mem = lower.mem(inst.ops, inst.data.x_cc.payload) },
|
||||
}),
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
}
|
||||
}
|
||||
|
||||
fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
const save_reg_list = lower.mir.extraData(Mir.SaveRegisterList, inst.data.payload).data;
|
||||
const base = @intToEnum(Register, save_reg_list.base_reg);
|
||||
var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
|
||||
const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*);
|
||||
for (callee_preserved_regs) |callee_preserved_reg| {
|
||||
if (!reg_list.isSet(callee_preserved_regs, callee_preserved_reg)) continue;
|
||||
const reg_op = Operand{ .reg = callee_preserved_reg };
|
||||
const mem_op = Operand{ .mem = Memory.sib(.qword, .{ .base = base, .disp = disp }) };
|
||||
try lower.emit(.none, .mov, switch (inst.tag) {
|
||||
.push_regs => &.{ mem_op, reg_op },
|
||||
.pop_regs => &.{ reg_op, mem_op },
|
||||
else => unreachable,
|
||||
});
|
||||
disp += 8;
|
||||
}
|
||||
}
|
||||
|
||||
fn mirLeaLinker(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
|
||||
const reg = @intToEnum(Register, metadata.reg);
|
||||
try lower.emit(.none, .lea, &.{
|
||||
.{ .reg = reg },
|
||||
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
|
||||
});
|
||||
}
|
||||
|
||||
const abi = @import("abi.zig");
|
||||
const assert = std.debug.assert;
|
||||
const bits = @import("bits.zig");
|
||||
const encoder = @import("encoder.zig");
|
||||
const std = @import("std");
|
||||
|
||||
const Air = @import("../../Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ErrorMsg = Module.ErrorMsg;
|
||||
const Immediate = bits.Immediate;
|
||||
const Instruction = encoder.Instruction;
|
||||
const Lower = @This();
|
||||
const Memory = bits.Memory;
|
||||
const Mir = @import("Mir.zig");
|
||||
const Mnemonic = Instruction.Mnemonic;
|
||||
const Module = @import("../../Module.zig");
|
||||
const Operand = Instruction.Operand;
|
||||
const Prefix = Instruction.Prefix;
|
||||
const Register = bits.Register;
|
||||
@ -655,16 +655,19 @@ pub const MemoryMoffs = struct {
|
||||
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 encode(seg: Register, offset: u64) MemoryMoffs {
|
||||
return .{
|
||||
.seg = @enumToInt(seg),
|
||||
.msb = @truncate(u32, offset >> 32),
|
||||
.lsb = @truncate(u32, offset >> 0),
|
||||
};
|
||||
}
|
||||
|
||||
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 fn decode(moffs: MemoryMoffs) Memory {
|
||||
return .{ .moffs = .{
|
||||
.seg = @intToEnum(Register, moffs.seg),
|
||||
.offset = @as(u64, moffs.msb) << 32 | @as(u64, moffs.lsb) << 0,
|
||||
} };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -515,7 +515,7 @@ pub const Memory = union(enum) {
|
||||
return switch (mem) {
|
||||
.rip => |r| r.ptr_size.bitSize(),
|
||||
.sib => |s| s.ptr_size.bitSize(),
|
||||
.moffs => unreachable,
|
||||
.moffs => 64,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user