Files
zig/src/arch/x86_64/bits.zig
2025-02-06 16:14:53 -05:00

847 lines
26 KiB
Zig

const std = @import("std");
const assert = std.debug.assert;
const expect = std.testing.expect;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
/// EFLAGS condition codes
pub const Condition = enum(u5) {
/// above
a,
/// above or equal
ae,
/// below
b,
/// below or equal
be,
/// carry
c,
/// equal
e,
/// greater
g,
/// greater or equal
ge,
/// less
l,
/// less or equal
le,
/// not above
na,
/// not above or equal
nae,
/// not below
nb,
/// not below or equal
nbe,
/// not carry
nc,
/// not equal
ne,
/// not greater
ng,
/// not greater or equal
nge,
/// not less
nl,
/// not less or equal
nle,
/// not overflow
no,
/// not parity
np,
/// not sign
ns,
/// not zero
nz,
/// overflow
o,
/// parity
p,
/// parity even
pe,
/// parity odd
po,
/// sign
s,
/// zero
z,
// Pseudo conditions
/// zero and not parity
z_and_np,
/// not zero or parity
nz_or_p,
/// Converts a std.math.CompareOperator into a condition flag,
/// i.e. returns the condition that is true iff the result of the
/// comparison is true. Assumes signed comparison
pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
return switch (op) {
.gte => .ge,
.gt => .g,
.neq => .ne,
.lt => .l,
.lte => .le,
.eq => .e,
};
}
/// Converts a std.math.CompareOperator into a condition flag,
/// i.e. returns the condition that is true iff the result of the
/// comparison is true. Assumes unsigned comparison
pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
return switch (op) {
.gte => .ae,
.gt => .a,
.neq => .ne,
.lt => .b,
.lte => .be,
.eq => .e,
};
}
pub fn fromCompareOperator(
signedness: std.builtin.Signedness,
op: std.math.CompareOperator,
) Condition {
return switch (signedness) {
.signed => fromCompareOperatorSigned(op),
.unsigned => fromCompareOperatorUnsigned(op),
};
}
/// Returns the condition which is true iff the given condition is false
pub fn negate(cond: Condition) Condition {
return switch (cond) {
.a => .na,
.ae => .nae,
.b => .nb,
.be => .nbe,
.c => .nc,
.e => .ne,
.g => .ng,
.ge => .nge,
.l => .nl,
.le => .nle,
.na => .a,
.nae => .ae,
.nb => .b,
.nbe => .be,
.nc => .c,
.ne => .e,
.ng => .g,
.nge => .ge,
.nl => .l,
.nle => .le,
.no => .o,
.np => .p,
.ns => .s,
.nz => .z,
.o => .no,
.p => .np,
.pe => .po,
.po => .pe,
.s => .ns,
.z => .nz,
.z_and_np => .nz_or_p,
.nz_or_p => .z_and_np,
};
}
/// Returns the equivalent condition when the operands are swapped.
pub fn commute(cond: Condition) Condition {
return switch (cond) {
else => cond,
.a => .b,
.ae => .be,
.b => .a,
.be => .ae,
.g => .l,
.ge => .le,
.l => .g,
.le => .ge,
.na => .nb,
.nae => .nbe,
.nb => .na,
.nbe => .nae,
.ng => .nl,
.nge => .nle,
.nl => .ng,
.nle => .nge,
};
}
};
/// The immediate operand of vcvtps2ph.
pub const RoundMode = packed struct(u5) {
direction: Direction = .mxcsr,
precision: enum(u1) {
normal = 0b0,
inexact = 0b1,
} = .normal,
pub const Direction = enum(u4) {
/// Round to nearest (even)
nearest = 0b0_00,
/// Round down (toward -∞)
down = 0b0_01,
/// Round up (toward +∞)
up = 0b0_10,
/// Round toward zero (truncate)
zero = 0b0_11,
/// Use current rounding mode of MXCSR.RC
mxcsr = 0b1_00,
};
pub fn imm(mode: RoundMode) Immediate {
return .u(@as(@typeInfo(RoundMode).@"struct".backing_integer.?, @bitCast(mode)));
}
};
/// The immediate operand of cmppd, cmpps, cmpsd, and cmpss.
pub const SseFloatPredicate = enum(u3) {
/// Equal (ordered, non-signaling)
eq,
/// Less-than (ordered, signaling)
lt,
/// Less-than-or-equal (ordered, signaling)
le,
/// Unordered (non-signaling)
unord,
/// Not-equal (unordered, non-signaling)
neq,
/// Not-less-than (unordered, signaling)
nlt,
/// Not-less-than-or-equal (unordered, signaling)
nle,
/// Ordered (non-signaling)
ord,
/// Equal (ordered, non-signaling)
pub const eq_oq: SseFloatPredicate = .eq;
/// Less-than (ordered, signaling)
pub const lt_os: SseFloatPredicate = .lt;
/// Less-than-or-equal (ordered, signaling)
pub const le_os: SseFloatPredicate = .le;
/// Unordered (non-signaling)
pub const unord_q: SseFloatPredicate = .unord;
/// Not-equal (unordered, non-signaling)
pub const neq_uq: SseFloatPredicate = .neq;
/// Not-less-than (unordered, signaling)
pub const nlt_us: SseFloatPredicate = .nlt;
/// Not-less-than-or-equal (unordered, signaling)
pub const nle_us: SseFloatPredicate = .nle;
/// Ordered (non-signaling)
pub const ord_q: SseFloatPredicate = .ord;
pub fn imm(pred: SseFloatPredicate) Immediate {
return .u(@intFromEnum(pred));
}
};
/// The immediate operand of vcmppd, vcmpps, vcmpsd, and vcmpss.
pub const VexFloatPredicate = enum(u5) {
/// Equal (ordered, non-signaling)
eq_oq,
/// Less-than (ordered, signaling)
lt_os,
/// Less-than-or-equal (ordered, signaling)
le_os,
/// Unordered (non-signaling)
unord_q,
/// Not-equal (unordered, non-signaling)
neq_uq,
/// Not-less-than (unordered, signaling)
nlt_us,
/// Not-less-than-or-equal (unordered, signaling)
nle_us,
/// Ordered (non-signaling)
ord_q,
/// Equal (unordered, non-signaling)
eq_uq,
/// Not-greater-than-or-equal (unordered, signaling)
nge_us,
/// Not-greater-than (unordered, signaling)
ngt_us,
/// False (ordered, non-signaling)
false_oq,
/// Not-equal (ordered, non-signaling)
neq_oq,
/// Greater-than-or-equal (ordered, signaling)
ge_os,
/// Greater-than (ordered, signaling)
gt_os,
/// True (unordered, non-signaling)
true_uq,
/// Equal (unordered, non-signaling)
eq_os,
/// Less-than (ordered, non-signaling)
lt_oq,
/// Less-than-or-equal (ordered, non-signaling)
le_oq,
/// Unordered (signaling)
unord_s,
/// Not-equal (unordered, signaling)
neq_us,
/// Not-less-than (unordered, non-signaling)
nlt_uq,
/// Not-less-than-or-equal (unordered, non-signaling)
nle_uq,
/// Ordered (signaling)
ord_s,
/// Equal (unordered, signaling)
eq_us,
/// Not-greater-than-or-equal (unordered, non-signaling)
nge_uq,
/// Not-greater-than (unordered, non-signaling)
ngt_uq,
/// False (ordered, signaling)
false_os,
/// Not-equal (ordered, signaling)
neq_os,
/// Greater-than-or-equal (ordered, non-signaling)
ge_oq,
/// Greater-than (ordered, non-signaling)
gt_oq,
/// True (unordered, signaling)
true_us,
/// Equal (ordered, non-signaling)
pub const eq: VexFloatPredicate = .eq_oq;
/// Less-than (ordered, signaling)
pub const lt: VexFloatPredicate = .lt_os;
/// Less-than-or-equal (ordered, signaling)
pub const le: VexFloatPredicate = .le_os;
/// Unordered (non-signaling)
pub const unord: VexFloatPredicate = .unord_q;
/// Not-equal (unordered, non-signaling)
pub const neq: VexFloatPredicate = .neq_uq;
/// Not-less-than (unordered, signaling)
pub const nlt: VexFloatPredicate = .nlt_us;
/// Not-less-than-or-equal (unordered, signaling)
pub const nle: VexFloatPredicate = .nle_us;
/// Ordered (non-signaling)
pub const ord: VexFloatPredicate = .ord_q;
/// Not-greater-than-or-equal (unordered, signaling)
pub const nge: VexFloatPredicate = .nge_us;
/// Not-greater-than (unordered, signaling)
pub const ngt: VexFloatPredicate = .ngt_us;
/// False (ordered, non-signaling)
pub const @"false": VexFloatPredicate = .false_oq;
/// Greater-than-or-equal (ordered, signaling)
pub const ge: VexFloatPredicate = .ge_os;
/// Greater-than (ordered, signaling)
pub const gt: VexFloatPredicate = .gt_os;
/// True (unordered, non-signaling)
pub const @"true": VexFloatPredicate = .true_uq;
pub fn imm(pred: VexFloatPredicate) Immediate {
return .u(@intFromEnum(pred));
}
};
pub const Register = enum(u8) {
// zig fmt: off
rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
r8, r9, r10, r11, r12, r13, r14, r15,
eax, ecx, edx, ebx, esp, ebp, esi, edi,
r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
ax, cx, dx, bx, sp, bp, si, di,
r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
al, cl, dl, bl, spl, bpl, sil, dil,
r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
ah, ch, dh, bh,
ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
st0, st1, st2, st3, st4, st5, st6, st7,
es, cs, ss, ds, fs, gs,
rip, eip, ip,
cr0, cr1, cr2, cr3, cr4, cr5, cr6, cr7,
cr8, cr9, cr10, cr11, cr12, cr13, cr14, cr15,
dr0, dr1, dr2, dr3, dr4, dr5, dr6, dr7,
dr8, dr9, dr10, dr11, dr12, dr13, dr14, dr15,
none,
// zig fmt: on
pub const Class = enum {
general_purpose,
segment,
x87,
mmx,
sse,
ip,
cr,
dr,
};
pub fn class(reg: Register) Class {
return switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.rax) ... @intFromEnum(Register.r15) => .general_purpose,
@intFromEnum(Register.eax) ... @intFromEnum(Register.r15d) => .general_purpose,
@intFromEnum(Register.ax) ... @intFromEnum(Register.r15w) => .general_purpose,
@intFromEnum(Register.al) ... @intFromEnum(Register.r15b) => .general_purpose,
@intFromEnum(Register.ah) ... @intFromEnum(Register.bh) => .general_purpose,
@intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => .sse,
@intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => .sse,
@intFromEnum(Register.mm0) ... @intFromEnum(Register.mm7) => .mmx,
@intFromEnum(Register.st0) ... @intFromEnum(Register.st7) => .x87,
@intFromEnum(Register.es) ... @intFromEnum(Register.gs) => .segment,
@intFromEnum(Register.rip) ... @intFromEnum(Register.ip) => .ip,
@intFromEnum(Register.cr0) ... @intFromEnum(Register.cr15) => .cr,
@intFromEnum(Register.dr0) ... @intFromEnum(Register.dr15) => .dr,
else => unreachable,
// zig fmt: on
};
}
pub fn id(reg: Register) u7 {
const base = switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.rax) ... @intFromEnum(Register.r15) => @intFromEnum(Register.rax),
@intFromEnum(Register.eax) ... @intFromEnum(Register.r15d) => @intFromEnum(Register.eax),
@intFromEnum(Register.ax) ... @intFromEnum(Register.r15w) => @intFromEnum(Register.ax),
@intFromEnum(Register.al) ... @intFromEnum(Register.r15b) => @intFromEnum(Register.al),
@intFromEnum(Register.ah) ... @intFromEnum(Register.bh) => @intFromEnum(Register.ah),
@intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => @intFromEnum(Register.ymm0) - 16,
@intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => @intFromEnum(Register.xmm0) - 16,
@intFromEnum(Register.mm0) ... @intFromEnum(Register.mm7) => @intFromEnum(Register.mm0) - 32,
@intFromEnum(Register.st0) ... @intFromEnum(Register.st7) => @intFromEnum(Register.st0) - 40,
@intFromEnum(Register.es) ... @intFromEnum(Register.gs) => @intFromEnum(Register.es) - 48,
@intFromEnum(Register.cr0) ... @intFromEnum(Register.cr15) => @intFromEnum(Register.cr0) - 54,
@intFromEnum(Register.dr0) ... @intFromEnum(Register.dr15) => @intFromEnum(Register.dr0) - 70,
else => unreachable,
// zig fmt: on
};
return @intCast(@intFromEnum(reg) - base);
}
pub fn bitSize(reg: Register) u10 {
return switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.rax) ... @intFromEnum(Register.r15) => 64,
@intFromEnum(Register.eax) ... @intFromEnum(Register.r15d) => 32,
@intFromEnum(Register.ax) ... @intFromEnum(Register.r15w) => 16,
@intFromEnum(Register.al) ... @intFromEnum(Register.r15b) => 8,
@intFromEnum(Register.ah) ... @intFromEnum(Register.bh) => 8,
@intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => 256,
@intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => 128,
@intFromEnum(Register.mm0) ... @intFromEnum(Register.mm7) => 64,
@intFromEnum(Register.st0) ... @intFromEnum(Register.st7) => 80,
@intFromEnum(Register.es) ... @intFromEnum(Register.gs) => 16,
@intFromEnum(Register.cr0) ... @intFromEnum(Register.cr15) => 64,
@intFromEnum(Register.dr0) ... @intFromEnum(Register.dr15) => 64,
else => unreachable,
// zig fmt: on
};
}
pub fn isExtended(reg: Register) bool {
return switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.r8) ... @intFromEnum(Register.r15) => true,
@intFromEnum(Register.r8d) ... @intFromEnum(Register.r15d) => true,
@intFromEnum(Register.r8w) ... @intFromEnum(Register.r15w) => true,
@intFromEnum(Register.r8b) ... @intFromEnum(Register.r15b) => true,
@intFromEnum(Register.ymm8) ... @intFromEnum(Register.ymm15) => true,
@intFromEnum(Register.xmm8) ... @intFromEnum(Register.xmm15) => true,
@intFromEnum(Register.cr8) ... @intFromEnum(Register.cr15) => true,
@intFromEnum(Register.dr8) ... @intFromEnum(Register.dr15) => true,
else => false,
// zig fmt: on
};
}
pub fn enc(reg: Register) u4 {
const base = switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.rax) ... @intFromEnum(Register.r15) => @intFromEnum(Register.rax),
@intFromEnum(Register.eax) ... @intFromEnum(Register.r15d) => @intFromEnum(Register.eax),
@intFromEnum(Register.ax) ... @intFromEnum(Register.r15w) => @intFromEnum(Register.ax),
@intFromEnum(Register.al) ... @intFromEnum(Register.r15b) => @intFromEnum(Register.al),
@intFromEnum(Register.ah) ... @intFromEnum(Register.bh) => @intFromEnum(Register.ah) - 4,
@intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => @intFromEnum(Register.ymm0),
@intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => @intFromEnum(Register.xmm0),
@intFromEnum(Register.mm0) ... @intFromEnum(Register.mm7) => @intFromEnum(Register.mm0),
@intFromEnum(Register.st0) ... @intFromEnum(Register.st7) => @intFromEnum(Register.st0),
@intFromEnum(Register.es) ... @intFromEnum(Register.gs) => @intFromEnum(Register.es),
@intFromEnum(Register.cr0) ... @intFromEnum(Register.cr15) => @intFromEnum(Register.cr0),
@intFromEnum(Register.dr0) ... @intFromEnum(Register.dr15) => @intFromEnum(Register.dr0),
else => unreachable,
// zig fmt: on
};
return @truncate(@intFromEnum(reg) - base);
}
pub fn lowEnc(reg: Register) u3 {
return @truncate(reg.enc());
}
pub fn toBitSize(reg: Register, bit_size: u64) Register {
return switch (bit_size) {
8 => reg.to8(),
16 => reg.to16(),
32 => reg.to32(),
64 => reg.to64(),
128 => reg.to128(),
256 => reg.to256(),
else => unreachable,
};
}
fn gpBase(reg: Register) u7 {
assert(reg.class() == .general_purpose);
return switch (@intFromEnum(reg)) {
// zig fmt: off
@intFromEnum(Register.rax) ... @intFromEnum(Register.r15) => @intFromEnum(Register.rax),
@intFromEnum(Register.eax) ... @intFromEnum(Register.r15d) => @intFromEnum(Register.eax),
@intFromEnum(Register.ax) ... @intFromEnum(Register.r15w) => @intFromEnum(Register.ax),
@intFromEnum(Register.al) ... @intFromEnum(Register.r15b) => @intFromEnum(Register.al),
@intFromEnum(Register.ah) ... @intFromEnum(Register.bh) => @intFromEnum(Register.ah),
else => unreachable,
// zig fmt: on
};
}
pub fn to64(reg: Register) Register {
return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.rax));
}
pub fn to32(reg: Register) Register {
return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.eax));
}
pub fn to16(reg: Register) Register {
return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.ax));
}
pub fn to8(reg: Register) Register {
return switch (@intFromEnum(reg)) {
else => @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.al)),
@intFromEnum(Register.ah)...@intFromEnum(Register.bh) => reg,
};
}
fn sseBase(reg: Register) u7 {
assert(reg.class() == .sse);
return switch (@intFromEnum(reg)) {
@intFromEnum(Register.ymm0)...@intFromEnum(Register.ymm15) => @intFromEnum(Register.ymm0),
@intFromEnum(Register.xmm0)...@intFromEnum(Register.xmm15) => @intFromEnum(Register.xmm0),
else => unreachable,
};
}
pub fn to256(reg: Register) Register {
return @enumFromInt(@intFromEnum(reg) - reg.sseBase() + @intFromEnum(Register.ymm0));
}
pub fn to128(reg: Register) Register {
return @enumFromInt(@intFromEnum(reg) - reg.sseBase() + @intFromEnum(Register.xmm0));
}
/// DWARF register encoding
pub fn dwarfNum(reg: Register) u6 {
return switch (reg.class()) {
.general_purpose => if (reg.isExtended())
reg.enc()
else
@as(u3, @truncate(@as(u24, 0o54673120) >> @as(u5, reg.enc()) * 3)),
.sse => 17 + @as(u6, reg.enc()),
.x87 => 33 + @as(u6, reg.enc()),
.mmx => 41 + @as(u6, reg.enc()),
.segment => 50 + @as(u6, reg.enc()),
.ip => 16,
.cr, .dr => unreachable,
};
}
};
test "Register id - different classes" {
try expect(Register.al.id() == Register.ax.id());
try expect(Register.ah.id() != Register.spl.id());
try expect(Register.ax.id() == Register.eax.id());
try expect(Register.eax.id() == Register.rax.id());
try expect(Register.ymm0.id() == 0b10000);
try expect(Register.ymm0.id() != Register.rax.id());
try expect(Register.xmm0.id() == Register.ymm0.id());
try expect(Register.xmm0.id() != Register.mm0.id());
try expect(Register.mm0.id() != Register.st0.id());
try expect(Register.es.id() == 0b110000);
}
test "Register enc - different classes" {
try expect(Register.al.enc() == Register.ax.enc());
try expect(Register.ah.enc() == Register.spl.enc());
try expect(Register.ax.enc() == Register.eax.enc());
try expect(Register.eax.enc() == Register.rax.enc());
try expect(Register.ymm0.enc() == Register.rax.enc());
try expect(Register.xmm0.enc() == Register.ymm0.enc());
try expect(Register.es.enc() == Register.rax.enc());
}
test "Register classes" {
try expect(Register.r11.class() == .general_purpose);
try expect(Register.ymm11.class() == .sse);
try expect(Register.mm3.class() == .mmx);
try expect(Register.st3.class() == .x87);
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 @intFromEnum(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(@intFromEnum(fi), fmt, options, writer, 0);
try writer.writeByte(')');
}
}
};
pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
pub const RegisterOffset = struct { reg: Register, off: i32 = 0 };
pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 };
pub const Memory = struct {
base: Base = .none,
mod: Mod = .{ .rm = .{} },
pub const Base = union(enum(u3)) {
none,
reg: Register,
frame: FrameIndex,
table,
reloc: u32,
pub const Tag = @typeInfo(Base).@"union".tag_type.?;
pub fn isExtended(self: Base) bool {
return switch (self) {
.none, .frame, .table, .reloc => false, // rsp, rbp, and rip are not extended
.reg => |reg| reg.isExtended(),
};
}
};
pub const Mod = union(enum(u1)) {
rm: Rm,
off: u64,
pub const Rm = struct {
size: Size = .none,
index: Register = .none,
scale: Scale = .@"1",
disp: i32 = 0,
};
};
pub const Size = enum(u4) {
none,
ptr,
gpr,
byte,
word,
dword,
qword,
tbyte,
xword,
yword,
zword,
pub fn fromSize(size: u32) Size {
return switch (size) {
1...1 => .byte,
2...2 => .word,
3...4 => .dword,
5...8 => .qword,
9...16 => .xword,
17...32 => .yword,
33...64 => .zword,
else => unreachable,
};
}
pub fn fromBitSize(bit_size: u64) Size {
return switch (bit_size) {
8 => .byte,
16 => .word,
32 => .dword,
64 => .qword,
80 => .tbyte,
128 => .xword,
256 => .yword,
512 => .zword,
else => unreachable,
};
}
pub fn bitSize(s: Size, target: *const std.Target) u64 {
return switch (s) {
.none => 0,
.ptr => target.ptrBitWidth(),
.gpr => switch (target.cpu.arch) {
else => unreachable,
.x86 => 32,
.x86_64 => 64,
},
.byte => 8,
.word => 16,
.dword => 32,
.qword => 64,
.tbyte => 80,
.xword => 128,
.yword => 256,
.zword => 512,
};
}
pub fn format(
s: Size,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
if (s == .none) return;
try writer.writeAll(@tagName(s));
switch (s) {
.none => unreachable,
.ptr, .gpr => {},
else => {
try writer.writeByte(' ');
try writer.writeAll("ptr");
},
}
}
};
pub const Scale = enum(u2) {
@"1",
@"2",
@"4",
@"8",
pub fn fromFactor(factor: u4) Scale {
return switch (factor) {
else => unreachable,
1 => .@"1",
2 => .@"2",
4 => .@"4",
8 => .@"8",
};
}
pub fn toFactor(scale: Scale) u4 {
return switch (scale) {
.@"1" => 1,
.@"2" => 2,
.@"4" => 4,
.@"8" => 8,
};
}
pub fn fromLog2(log2: u2) Scale {
return @enumFromInt(log2);
}
pub fn toLog2(scale: Scale) u2 {
return @intFromEnum(scale);
}
};
};
pub const Immediate = union(enum) {
signed: i32,
unsigned: u64,
reloc: SymbolOffset,
pub fn u(x: u64) Immediate {
return .{ .unsigned = x };
}
pub fn s(x: i32) Immediate {
return .{ .signed = x };
}
pub fn rel(sym_off: SymbolOffset) Immediate {
return .{ .reloc = sym_off };
}
pub fn format(
imm: Immediate,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
switch (imm) {
inline else => |int| try writer.print("{d}", .{int}),
.reloc => |sym_off| try writer.print("Symbol({[sym_index]d}) + {[off]d}", sym_off),
}
}
};