mirror of
https://github.com/ziglang/zig.git
synced 2026-01-09 08:55:36 +00:00
x86_64: add frame indices
This allows abstract references to the stack frame which enables: * deferred frame layout until after the function has been processed * frame compaction (sorting by alignment) * being able to overalign the stack * references that change based on an overaligned stack * moving callee saved spills to the usual part (smaller code size) * shared call frame (avoids stack adjustments before and after calls)
This commit is contained in:
parent
f618398b24
commit
26d4f9b69e
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,9 @@ prev_di_pc: usize,
|
||||
code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
|
||||
relocs: std.ArrayListUnmanaged(Reloc) = .{},
|
||||
|
||||
pub const Error = Lower.Error || error{EmitFail};
|
||||
pub const Error = Lower.Error || error{
|
||||
EmitFail,
|
||||
};
|
||||
|
||||
pub fn emitMir(emit: *Emit) Error!void {
|
||||
for (0..emit.lower.mir.instructions.len) |i| {
|
||||
@ -22,7 +24,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
|
||||
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());
|
||||
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) {
|
||||
@ -120,11 +122,10 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.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_line => try emit.dbgAdvancePCAndLine(
|
||||
inst.data.line_column.line,
|
||||
inst.data.line_column.column,
|
||||
),
|
||||
|
||||
.dbg_prologue_end => {
|
||||
switch (emit.debug_output) {
|
||||
|
||||
@ -47,17 +47,8 @@ pub fn findByMnemonic(
|
||||
},
|
||||
else => {},
|
||||
} else false;
|
||||
const rex_extended = for (ops) |op| switch (op) {
|
||||
.reg => |r| if (r.isExtended()) break true,
|
||||
.mem => |m| {
|
||||
if (m.base()) |base| {
|
||||
if (base.isExtended()) break true;
|
||||
}
|
||||
if (m.scaleIndex()) |si| {
|
||||
if (si.index.isExtended()) break true;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
const rex_extended = for (ops) |op| {
|
||||
if (op.isBaseExtended() or op.isIndexExtended()) break true;
|
||||
} else false;
|
||||
|
||||
if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode;
|
||||
@ -544,7 +535,7 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
|
||||
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.
|
||||
inst.encode(cwriter.writer(), .{ .allow_frame_loc = true }) catch unreachable; // Not allowed to fail here unless OOM.
|
||||
return @intCast(usize, cwriter.bytes_written);
|
||||
}
|
||||
|
||||
|
||||
@ -136,7 +136,8 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
|
||||
.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),
|
||||
.push_regs => try lower.mirPushPopRegisterList(inst, .push),
|
||||
.pop_regs => try lower.mirPushPopRegisterList(inst, .pop),
|
||||
|
||||
.dbg_line,
|
||||
.dbg_prologue_end,
|
||||
@ -190,7 +191,7 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
|
||||
}
|
||||
|
||||
fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
|
||||
return switch (ops) {
|
||||
return lower.mir.resolveFrameLoc(switch (ops) {
|
||||
.rm_sib,
|
||||
.rm_sib_cc,
|
||||
.m_sib,
|
||||
@ -227,7 +228,7 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
|
||||
=> lower.mir.extraData(Mir.MemoryMoffs, payload).data.decode(),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
|
||||
@ -417,23 +418,15 @@ fn mirSetcc(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst, comptime mnemonic: Mnemonic) Error!void {
|
||||
const reg_list = Mir.RegisterList.fromInt(inst.data.payload);
|
||||
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;
|
||||
}
|
||||
var it = reg_list.iterator(.{ .direction = switch (mnemonic) {
|
||||
.push => .reverse,
|
||||
.pop => .forward,
|
||||
else => unreachable,
|
||||
} });
|
||||
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
|
||||
}
|
||||
|
||||
fn mirLeaLinker(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
|
||||
@ -23,6 +23,7 @@ const Register = bits.Register;
|
||||
instructions: std.MultiArrayList(Inst).Slice,
|
||||
/// The meaning of this data is determined by `Inst.Tag` value.
|
||||
extra: []const u32,
|
||||
frame_locs: std.MultiArrayList(FrameLoc).Slice,
|
||||
|
||||
pub const Inst = struct {
|
||||
tag: Tag,
|
||||
@ -241,13 +242,13 @@ pub const Inst = struct {
|
||||
/// Start of epilogue
|
||||
dbg_epilogue_begin,
|
||||
/// Update debug line
|
||||
/// Uses `payload` payload with data of type `DbgLineColumn`.
|
||||
/// Uses `line_column` payload containing the line and column.
|
||||
dbg_line,
|
||||
/// Push registers
|
||||
/// Uses `payload` payload with data of type `SaveRegisterList`.
|
||||
/// Uses `payload` payload containing `RegisterList.asInt` directly.
|
||||
push_regs,
|
||||
/// Pop registers
|
||||
/// Uses `payload` payload with data of type `SaveRegisterList`.
|
||||
/// Uses `payload` payload containing `RegisterList.asInt` directly.
|
||||
pop_regs,
|
||||
|
||||
/// Tombstone
|
||||
@ -500,6 +501,11 @@ pub const Inst = struct {
|
||||
/// Index into the linker's symbol table.
|
||||
sym_index: u32,
|
||||
},
|
||||
/// Debug line and column position
|
||||
line_column: struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
},
|
||||
/// Index into `extra`. Meaning of what can be found there is context-dependent.
|
||||
payload: u32,
|
||||
};
|
||||
@ -522,12 +528,11 @@ pub const LeaRegisterReloc = struct {
|
||||
sym_index: u32,
|
||||
};
|
||||
|
||||
/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers
|
||||
/// in a compact manner.
|
||||
/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
|
||||
pub const RegisterList = struct {
|
||||
bitset: BitSet = BitSet.initEmpty(),
|
||||
|
||||
const BitSet = IntegerBitSet(@ctz(@as(u32, 0)));
|
||||
const BitSet = IntegerBitSet(32);
|
||||
const Self = @This();
|
||||
|
||||
fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
|
||||
@ -547,6 +552,10 @@ pub const RegisterList = struct {
|
||||
return self.bitset.isSet(index);
|
||||
}
|
||||
|
||||
pub fn iterator(self: Self, comptime options: std.bit_set.IteratorOptions) BitSet.Iterator(options) {
|
||||
return self.bitset.iterator(options);
|
||||
}
|
||||
|
||||
pub fn asInt(self: Self) u32 {
|
||||
return self.bitset.mask;
|
||||
}
|
||||
@ -562,14 +571,6 @@ pub const RegisterList = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const SaveRegisterList = struct {
|
||||
/// Base register
|
||||
base_reg: u32,
|
||||
/// Use `RegisterList` to populate.
|
||||
register_list: u32,
|
||||
stack_end: u32,
|
||||
};
|
||||
|
||||
pub const Imm64 = struct {
|
||||
msb: u32,
|
||||
lsb: u32,
|
||||
@ -593,41 +594,51 @@ pub const Imm64 = 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,
|
||||
/// Base register tag of type Memory.Base.Tag
|
||||
base_tag: u32,
|
||||
/// Base register of type Register or FrameIndex
|
||||
base: u32,
|
||||
/// Scale starting at bit 0 and index register starting at bit 4.
|
||||
scale_index: u32,
|
||||
/// Displacement value.
|
||||
disp: i32,
|
||||
|
||||
pub fn encode(mem: Memory) MemorySib {
|
||||
const sib = mem.sib;
|
||||
assert(sib.scale_index.scale == 0 or std.math.isPowerOfTwo(sib.scale_index.scale));
|
||||
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,
|
||||
.base_tag = @enumToInt(@as(Memory.Base.Tag, sib.base)),
|
||||
.base = switch (sib.base) {
|
||||
.none => undefined,
|
||||
.reg => |r| @enumToInt(r),
|
||||
.frame => |fi| @enumToInt(fi),
|
||||
},
|
||||
.scale_index = @as(u32, sib.scale_index.scale) << 0 |
|
||||
@as(u32, if (sib.scale_index.scale > 0)
|
||||
@enumToInt(sib.scale_index.index)
|
||||
else
|
||||
undefined) << 4,
|
||||
.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 = .{
|
||||
const scale = @truncate(u4, msib.scale_index);
|
||||
assert(scale == 0 or std.math.isPowerOfTwo(scale));
|
||||
return .{ .sib = .{
|
||||
.ptr_size = @intToEnum(Memory.PtrSize, msib.ptr_size),
|
||||
.base = base,
|
||||
.scale_index = scale_index,
|
||||
.base = switch (@intToEnum(Memory.Base.Tag, msib.base_tag)) {
|
||||
.none => .none,
|
||||
.reg => .{ .reg = @intToEnum(Register, msib.base) },
|
||||
.frame => .{ .frame = @intToEnum(bits.FrameIndex, msib.base) },
|
||||
},
|
||||
.scale_index = .{
|
||||
.scale = scale,
|
||||
.index = if (scale > 0) @intToEnum(Register, msib.scale_index >> 4) else undefined,
|
||||
},
|
||||
.disp = msib.disp,
|
||||
} };
|
||||
return mem;
|
||||
}
|
||||
};
|
||||
|
||||
@ -676,14 +687,10 @@ pub const MemoryMoffs = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const DbgLineColumn = struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
};
|
||||
|
||||
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
|
||||
mir.instructions.deinit(gpa);
|
||||
gpa.free(mir.extra);
|
||||
mir.frame_locs.deinit(gpa);
|
||||
mir.* = undefined;
|
||||
}
|
||||
|
||||
@ -704,3 +711,22 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end:
|
||||
.end = i,
|
||||
};
|
||||
}
|
||||
|
||||
pub const FrameLoc = struct {
|
||||
base: Register,
|
||||
disp: i32,
|
||||
};
|
||||
|
||||
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
|
||||
return switch (mem) {
|
||||
.sib => |sib| switch (sib.base) {
|
||||
.none, .reg => mem,
|
||||
.frame => |index| if (mir.frame_locs.len > 0) Memory.sib(sib.ptr_size, .{
|
||||
.base = .{ .reg = mir.frame_locs.items(.base)[@enumToInt(index)] },
|
||||
.disp = mir.frame_locs.items(.disp)[@enumToInt(index)] + sib.disp,
|
||||
.scale_index = mem.scaleIndex(),
|
||||
}) else mem,
|
||||
},
|
||||
.rip, .moffs => mem,
|
||||
};
|
||||
}
|
||||
|
||||
@ -405,14 +405,69 @@ test "Register classes" {
|
||||
try expect(Register.fs.class() == .segment);
|
||||
}
|
||||
|
||||
pub const FrameIndex = enum(u32) {
|
||||
// This index refers to the start of the arguments passed to this function
|
||||
args_frame,
|
||||
// This index refers to the return address pushed by a `call` and popped by a `ret`.
|
||||
ret_addr,
|
||||
// This index refers to the base pointer pushed in the prologue and popped in the epilogue.
|
||||
base_ptr,
|
||||
// This index refers to the entire stack frame.
|
||||
stack_frame,
|
||||
// This index refers to the start of the call frame for arguments passed to called functions
|
||||
call_frame,
|
||||
// Other indices are used for local variable stack slots
|
||||
_,
|
||||
|
||||
pub const named_count = @typeInfo(FrameIndex).Enum.fields.len;
|
||||
|
||||
pub fn isNamed(fi: FrameIndex) bool {
|
||||
return @enumToInt(fi) < named_count;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
fi: FrameIndex,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
try writer.writeAll("FrameIndex");
|
||||
if (fi.isNamed()) {
|
||||
try writer.writeByte('.');
|
||||
try writer.writeAll(@tagName(fi));
|
||||
} else {
|
||||
try writer.writeByte('(');
|
||||
try std.fmt.formatType(@enumToInt(fi), fmt, options, writer, 0);
|
||||
try writer.writeByte(')');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Memory = union(enum) {
|
||||
sib: Sib,
|
||||
rip: Rip,
|
||||
moffs: Moffs,
|
||||
|
||||
pub const ScaleIndex = packed struct {
|
||||
pub const Base = union(enum) {
|
||||
none,
|
||||
reg: Register,
|
||||
frame: FrameIndex,
|
||||
|
||||
pub const Tag = @typeInfo(Base).Union.tag_type.?;
|
||||
|
||||
pub fn isExtended(self: Base) bool {
|
||||
return switch (self) {
|
||||
.none, .frame => false, // neither rsp nor rbp are extended
|
||||
.reg => |reg| reg.isExtended(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ScaleIndex = struct {
|
||||
scale: u4,
|
||||
index: Register,
|
||||
|
||||
const none = ScaleIndex{ .scale = 0, .index = undefined };
|
||||
};
|
||||
|
||||
pub const PtrSize = enum {
|
||||
@ -460,8 +515,8 @@ pub const Memory = union(enum) {
|
||||
|
||||
pub const Sib = struct {
|
||||
ptr_size: PtrSize,
|
||||
base: ?Register,
|
||||
scale_index: ?ScaleIndex,
|
||||
base: Base,
|
||||
scale_index: ScaleIndex,
|
||||
disp: i32,
|
||||
};
|
||||
|
||||
@ -482,7 +537,7 @@ pub const Memory = union(enum) {
|
||||
|
||||
pub fn sib(ptr_size: PtrSize, args: struct {
|
||||
disp: i32 = 0,
|
||||
base: ?Register = null,
|
||||
base: Base = .none,
|
||||
scale_index: ?ScaleIndex = null,
|
||||
}) Memory {
|
||||
if (args.scale_index) |si| assert(std.math.isPowerOfTwo(si.scale));
|
||||
@ -490,7 +545,7 @@ pub const Memory = union(enum) {
|
||||
.base = args.base,
|
||||
.disp = args.disp,
|
||||
.ptr_size = ptr_size,
|
||||
.scale_index = args.scale_index,
|
||||
.scale_index = if (args.scale_index) |si| si else ScaleIndex.none,
|
||||
} };
|
||||
}
|
||||
|
||||
@ -502,22 +557,25 @@ pub const Memory = union(enum) {
|
||||
return switch (mem) {
|
||||
.moffs => true,
|
||||
.rip => false,
|
||||
.sib => |s| if (s.base) |r| r.class() == .segment else false,
|
||||
.sib => |s| switch (s.base) {
|
||||
.none, .frame => false,
|
||||
.reg => |reg| reg.class() == .segment,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn base(mem: Memory) ?Register {
|
||||
pub fn base(mem: Memory) Base {
|
||||
return switch (mem) {
|
||||
.moffs => |m| m.seg,
|
||||
.moffs => |m| .{ .reg = m.seg },
|
||||
.sib => |s| s.base,
|
||||
.rip => null,
|
||||
.rip => .none,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scaleIndex(mem: Memory) ?ScaleIndex {
|
||||
return switch (mem) {
|
||||
.moffs, .rip => null,
|
||||
.sib => |s| s.scale_index,
|
||||
.sib => |s| if (s.scale_index.scale > 0) s.scale_index else null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,21 @@ pub const Instruction = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isBaseExtended(op: Operand) bool {
|
||||
return switch (op) {
|
||||
.none, .imm => false,
|
||||
.reg => |reg| reg.isExtended(),
|
||||
.mem => |mem| mem.base().isExtended(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isIndexExtended(op: Operand) bool {
|
||||
return switch (op) {
|
||||
.none, .reg, .imm => false,
|
||||
.mem => |mem| if (mem.scaleIndex()) |si| si.index.isExtended() else false,
|
||||
};
|
||||
}
|
||||
|
||||
fn format(
|
||||
op: Operand,
|
||||
comptime unused_format_string: []const u8,
|
||||
@ -98,17 +113,24 @@ pub const Instruction = struct {
|
||||
try writer.print("{s} ptr ", .{@tagName(sib.ptr_size)});
|
||||
|
||||
if (mem.isSegmentRegister()) {
|
||||
return writer.print("{s}:0x{x}", .{ @tagName(sib.base.?), sib.disp });
|
||||
return writer.print("{s}:0x{x}", .{ @tagName(sib.base.reg), sib.disp });
|
||||
}
|
||||
|
||||
try writer.writeByte('[');
|
||||
|
||||
var any = false;
|
||||
if (sib.base) |base| {
|
||||
try writer.print("{s}", .{@tagName(base)});
|
||||
any = true;
|
||||
switch (sib.base) {
|
||||
.none => {},
|
||||
.reg => |reg| {
|
||||
try writer.print("{s}", .{@tagName(reg)});
|
||||
any = true;
|
||||
},
|
||||
.frame => |frame| {
|
||||
try writer.print("{}", .{frame});
|
||||
any = true;
|
||||
},
|
||||
}
|
||||
if (sib.scale_index) |si| {
|
||||
if (mem.scaleIndex()) |si| {
|
||||
if (any) try writer.writeAll(" + ");
|
||||
try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale });
|
||||
any = true;
|
||||
@ -182,8 +204,8 @@ pub const Instruction = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(inst: Instruction, writer: anytype) !void {
|
||||
const encoder = Encoder(@TypeOf(writer)){ .writer = writer };
|
||||
pub fn encode(inst: Instruction, writer: anytype, comptime opts: Options) !void {
|
||||
const encoder = Encoder(@TypeOf(writer), opts){ .writer = writer };
|
||||
const enc = inst.encoding;
|
||||
const data = enc.data;
|
||||
|
||||
@ -269,22 +291,24 @@ pub const Instruction = struct {
|
||||
|
||||
const segment_override: ?Register = switch (op_en) {
|
||||
.i, .zi, .o, .oi, .d, .np => null,
|
||||
.fd => inst.ops[1].mem.base().?,
|
||||
.td => inst.ops[0].mem.base().?,
|
||||
.rm, .rmi => if (inst.ops[1].isSegmentRegister()) blk: {
|
||||
break :blk switch (inst.ops[1]) {
|
||||
.reg => |r| r,
|
||||
.mem => |m| m.base().?,
|
||||
.fd => inst.ops[1].mem.base().reg,
|
||||
.td => inst.ops[0].mem.base().reg,
|
||||
.rm, .rmi => if (inst.ops[1].isSegmentRegister())
|
||||
switch (inst.ops[1]) {
|
||||
.reg => |reg| reg,
|
||||
.mem => |mem| mem.base().reg,
|
||||
else => unreachable,
|
||||
};
|
||||
} else null,
|
||||
.m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.ops[0].isSegmentRegister()) blk: {
|
||||
break :blk switch (inst.ops[0]) {
|
||||
.reg => |r| r,
|
||||
.mem => |m| m.base().?,
|
||||
}
|
||||
else
|
||||
null,
|
||||
.m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.ops[0].isSegmentRegister())
|
||||
switch (inst.ops[0]) {
|
||||
.reg => |reg| reg,
|
||||
.mem => |mem| mem.base().reg,
|
||||
else => unreachable,
|
||||
};
|
||||
} else null,
|
||||
}
|
||||
else
|
||||
null,
|
||||
};
|
||||
if (segment_override) |seg| {
|
||||
legacy.setSegmentOverride(seg);
|
||||
@ -307,27 +331,17 @@ pub const Instruction = struct {
|
||||
const r_op = switch (op_en) {
|
||||
.rm, .rmi => inst.ops[0],
|
||||
.mr, .mri, .mrc => inst.ops[1],
|
||||
else => null,
|
||||
else => .none,
|
||||
};
|
||||
if (r_op) |op| {
|
||||
rex.r = op.reg.isExtended();
|
||||
}
|
||||
rex.r = r_op.isBaseExtended();
|
||||
|
||||
const b_x_op = switch (op_en) {
|
||||
.rm, .rmi => inst.ops[1],
|
||||
.m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0],
|
||||
else => unreachable,
|
||||
};
|
||||
switch (b_x_op) {
|
||||
.reg => |r| {
|
||||
rex.b = r.isExtended();
|
||||
},
|
||||
.mem => |mem| {
|
||||
rex.b = if (mem.base()) |base| base.isExtended() else false;
|
||||
rex.x = if (mem.scaleIndex()) |si| si.index.isExtended() else false;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
rex.b = b_x_op.isBaseExtended();
|
||||
rex.x = b_x_op.isIndexExtended();
|
||||
},
|
||||
}
|
||||
|
||||
@ -348,72 +362,75 @@ pub const Instruction = struct {
|
||||
|
||||
switch (mem) {
|
||||
.moffs => unreachable,
|
||||
.sib => |sib| {
|
||||
if (sib.base) |base| {
|
||||
if (base.class() == .segment) {
|
||||
// TODO audit this wrt SIB
|
||||
try encoder.modRm_SIBDisp0(operand_enc);
|
||||
if (sib.scale_index) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
|
||||
} else {
|
||||
try encoder.sib_disp32();
|
||||
}
|
||||
try encoder.disp32(sib.disp);
|
||||
} else {
|
||||
assert(base.class() == .general_purpose);
|
||||
const dst = base.lowEnc();
|
||||
const src = operand_enc;
|
||||
if (dst == 4 or sib.scale_index != null) {
|
||||
if (sib.disp == 0 and dst != 5) {
|
||||
try encoder.modRm_SIBDisp0(src);
|
||||
if (sib.scale_index) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_base(dst);
|
||||
}
|
||||
} else if (math.cast(i8, sib.disp)) |_| {
|
||||
try encoder.modRm_SIBDisp8(src);
|
||||
if (sib.scale_index) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_baseDisp8(dst);
|
||||
}
|
||||
try encoder.disp8(@truncate(i8, sib.disp));
|
||||
} else {
|
||||
try encoder.modRm_SIBDisp32(src);
|
||||
if (sib.scale_index) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_baseDisp32(dst);
|
||||
}
|
||||
try encoder.disp32(sib.disp);
|
||||
}
|
||||
} else {
|
||||
if (sib.disp == 0 and dst != 5) {
|
||||
try encoder.modRm_indirectDisp0(src, dst);
|
||||
} else if (math.cast(i8, sib.disp)) |_| {
|
||||
try encoder.modRm_indirectDisp8(src, dst);
|
||||
try encoder.disp8(@truncate(i8, sib.disp));
|
||||
} else {
|
||||
try encoder.modRm_indirectDisp32(src, dst);
|
||||
try encoder.disp32(sib.disp);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
.sib => |sib| switch (sib.base) {
|
||||
.none => {
|
||||
try encoder.modRm_SIBDisp0(operand_enc);
|
||||
if (sib.scale_index) |si| {
|
||||
if (mem.scaleIndex()) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
|
||||
} else {
|
||||
try encoder.sib_disp32();
|
||||
}
|
||||
try encoder.disp32(sib.disp);
|
||||
}
|
||||
},
|
||||
.reg => |base| if (base.class() == .segment) {
|
||||
// TODO audit this wrt SIB
|
||||
try encoder.modRm_SIBDisp0(operand_enc);
|
||||
if (mem.scaleIndex()) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
|
||||
} else {
|
||||
try encoder.sib_disp32();
|
||||
}
|
||||
try encoder.disp32(sib.disp);
|
||||
} else {
|
||||
assert(base.class() == .general_purpose);
|
||||
const dst = base.lowEnc();
|
||||
const src = operand_enc;
|
||||
if (dst == 4 or mem.scaleIndex() != null) {
|
||||
if (sib.disp == 0 and dst != 5) {
|
||||
try encoder.modRm_SIBDisp0(src);
|
||||
if (mem.scaleIndex()) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_base(dst);
|
||||
}
|
||||
} else if (math.cast(i8, sib.disp)) |_| {
|
||||
try encoder.modRm_SIBDisp8(src);
|
||||
if (mem.scaleIndex()) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_baseDisp8(dst);
|
||||
}
|
||||
try encoder.disp8(@truncate(i8, sib.disp));
|
||||
} else {
|
||||
try encoder.modRm_SIBDisp32(src);
|
||||
if (mem.scaleIndex()) |si| {
|
||||
const scale = math.log2_int(u4, si.scale);
|
||||
try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
|
||||
} else {
|
||||
try encoder.sib_baseDisp32(dst);
|
||||
}
|
||||
try encoder.disp32(sib.disp);
|
||||
}
|
||||
} else {
|
||||
if (sib.disp == 0 and dst != 5) {
|
||||
try encoder.modRm_indirectDisp0(src, dst);
|
||||
} else if (math.cast(i8, sib.disp)) |_| {
|
||||
try encoder.modRm_indirectDisp8(src, dst);
|
||||
try encoder.disp8(@truncate(i8, sib.disp));
|
||||
} else {
|
||||
try encoder.modRm_indirectDisp32(src, dst);
|
||||
try encoder.disp32(sib.disp);
|
||||
}
|
||||
}
|
||||
},
|
||||
.frame => if (@TypeOf(encoder).options.allow_frame_loc) {
|
||||
try encoder.modRm_indirectDisp32(operand_enc, undefined);
|
||||
try encoder.disp32(undefined);
|
||||
} else return error.CannotEncode,
|
||||
},
|
||||
.rip => |rip| {
|
||||
try encoder.modRm_RIPDisp32(operand_enc);
|
||||
@ -482,11 +499,14 @@ pub const LegacyPrefixes = packed struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn Encoder(comptime T: type) type {
|
||||
pub const Options = struct { allow_frame_loc: bool = false };
|
||||
|
||||
fn Encoder(comptime T: type, comptime opts: Options) type {
|
||||
return struct {
|
||||
writer: T,
|
||||
|
||||
const Self = @This();
|
||||
pub const options = opts;
|
||||
|
||||
// --------
|
||||
// Prefixes
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user