master
  1const std = @import("std");
  2const assert = std.debug.assert;
  3const expect = std.testing.expect;
  4
  5const Allocator = std.mem.Allocator;
  6const ArrayList = std.ArrayList;
  7const InternPool = @import("../../InternPool.zig");
  8const link = @import("../../link.zig");
  9const Mir = @import("Mir.zig");
 10
 11/// EFLAGS condition codes
 12pub const Condition = enum(u5) {
 13    /// above
 14    a,
 15    /// above or equal
 16    ae,
 17    /// below
 18    b,
 19    /// below or equal
 20    be,
 21    /// carry
 22    c,
 23    /// equal
 24    e,
 25    /// greater
 26    g,
 27    /// greater or equal
 28    ge,
 29    /// less
 30    l,
 31    /// less or equal
 32    le,
 33    /// not above
 34    na,
 35    /// not above or equal
 36    nae,
 37    /// not below
 38    nb,
 39    /// not below or equal
 40    nbe,
 41    /// not carry
 42    nc,
 43    /// not equal
 44    ne,
 45    /// not greater
 46    ng,
 47    /// not greater or equal
 48    nge,
 49    /// not less
 50    nl,
 51    /// not less or equal
 52    nle,
 53    /// not overflow
 54    no,
 55    /// not parity
 56    np,
 57    /// not sign
 58    ns,
 59    /// not zero
 60    nz,
 61    /// overflow
 62    o,
 63    /// parity
 64    p,
 65    /// parity even
 66    pe,
 67    /// parity odd
 68    po,
 69    /// sign
 70    s,
 71    /// zero
 72    z,
 73
 74    // Pseudo conditions
 75    /// zero and not parity
 76    z_and_np,
 77    /// not zero or parity
 78    nz_or_p,
 79
 80    /// Converts a std.math.CompareOperator into a condition flag,
 81    /// i.e. returns the condition that is true iff the result of the
 82    /// comparison is true. Assumes signed comparison
 83    pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
 84        return switch (op) {
 85            .gte => .ge,
 86            .gt => .g,
 87            .neq => .ne,
 88            .lt => .l,
 89            .lte => .le,
 90            .eq => .e,
 91        };
 92    }
 93
 94    /// Converts a std.math.CompareOperator into a condition flag,
 95    /// i.e. returns the condition that is true iff the result of the
 96    /// comparison is true. Assumes unsigned comparison
 97    pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
 98        return switch (op) {
 99            .gte => .ae,
100            .gt => .a,
101            .neq => .ne,
102            .lt => .b,
103            .lte => .be,
104            .eq => .e,
105        };
106    }
107
108    pub fn fromCompareOperator(
109        signedness: std.builtin.Signedness,
110        op: std.math.CompareOperator,
111    ) Condition {
112        return switch (signedness) {
113            .signed => fromCompareOperatorSigned(op),
114            .unsigned => fromCompareOperatorUnsigned(op),
115        };
116    }
117
118    /// Returns the condition which is true iff the given condition is false
119    pub fn negate(cond: Condition) Condition {
120        return switch (cond) {
121            .a => .na,
122            .ae => .nae,
123            .b => .nb,
124            .be => .nbe,
125            .c => .nc,
126            .e => .ne,
127            .g => .ng,
128            .ge => .nge,
129            .l => .nl,
130            .le => .nle,
131            .na => .a,
132            .nae => .ae,
133            .nb => .b,
134            .nbe => .be,
135            .nc => .c,
136            .ne => .e,
137            .ng => .g,
138            .nge => .ge,
139            .nl => .l,
140            .nle => .le,
141            .no => .o,
142            .np => .p,
143            .ns => .s,
144            .nz => .z,
145            .o => .no,
146            .p => .np,
147            .pe => .po,
148            .po => .pe,
149            .s => .ns,
150            .z => .nz,
151
152            .z_and_np => .nz_or_p,
153            .nz_or_p => .z_and_np,
154        };
155    }
156
157    /// Returns the equivalent condition when the operands are swapped.
158    pub fn commute(cond: Condition) Condition {
159        return switch (cond) {
160            else => cond,
161            .a => .b,
162            .ae => .be,
163            .b => .a,
164            .be => .ae,
165            .g => .l,
166            .ge => .le,
167            .l => .g,
168            .le => .ge,
169            .na => .nb,
170            .nae => .nbe,
171            .nb => .na,
172            .nbe => .nae,
173            .ng => .nl,
174            .nge => .nle,
175            .nl => .ng,
176            .nle => .nge,
177        };
178    }
179};
180
181/// The immediate operand of vcvtps2ph.
182pub const RoundMode = packed struct(u5) {
183    direction: Direction = .mxcsr,
184    precision: enum(u1) {
185        normal = 0b0,
186        inexact = 0b1,
187    } = .normal,
188
189    pub const Direction = enum(u4) {
190        /// Round to nearest (even)
191        nearest = 0b0_00,
192        /// Round down (toward -∞)
193        down = 0b0_01,
194        /// Round up (toward +∞)
195        up = 0b0_10,
196        /// Round toward zero (truncate)
197        zero = 0b0_11,
198        /// Use current rounding mode of MXCSR.RC
199        mxcsr = 0b1_00,
200    };
201
202    pub fn imm(mode: RoundMode) Immediate {
203        return .u(@as(@typeInfo(RoundMode).@"struct".backing_integer.?, @bitCast(mode)));
204    }
205};
206
207/// The immediate operand of cmppd, cmpps, cmpsd, and cmpss.
208pub const SseFloatPredicate = enum(u3) {
209    /// Equal (ordered, non-signaling)
210    eq,
211    /// Less-than (ordered, signaling)
212    lt,
213    /// Less-than-or-equal (ordered, signaling)
214    le,
215    /// Unordered (non-signaling)
216    unord,
217    /// Not-equal (unordered, non-signaling)
218    neq,
219    /// Not-less-than (unordered, signaling)
220    nlt,
221    /// Not-less-than-or-equal (unordered, signaling)
222    nle,
223    /// Ordered (non-signaling)
224    ord,
225
226    /// Equal (ordered, non-signaling)
227    pub const eq_oq: SseFloatPredicate = .eq;
228    /// Less-than (ordered, signaling)
229    pub const lt_os: SseFloatPredicate = .lt;
230    /// Less-than-or-equal (ordered, signaling)
231    pub const le_os: SseFloatPredicate = .le;
232    /// Unordered (non-signaling)
233    pub const unord_q: SseFloatPredicate = .unord;
234    /// Not-equal (unordered, non-signaling)
235    pub const neq_uq: SseFloatPredicate = .neq;
236    /// Not-less-than (unordered, signaling)
237    pub const nlt_us: SseFloatPredicate = .nlt;
238    /// Not-less-than-or-equal (unordered, signaling)
239    pub const nle_us: SseFloatPredicate = .nle;
240    /// Ordered (non-signaling)
241    pub const ord_q: SseFloatPredicate = .ord;
242
243    pub fn imm(pred: SseFloatPredicate) Immediate {
244        return .u(@intFromEnum(pred));
245    }
246};
247
248/// The immediate operand of vcmppd, vcmpps, vcmpsd, and vcmpss.
249pub const VexFloatPredicate = enum(u5) {
250    /// Equal (ordered, non-signaling)
251    eq_oq,
252    /// Less-than (ordered, signaling)
253    lt_os,
254    /// Less-than-or-equal (ordered, signaling)
255    le_os,
256    /// Unordered (non-signaling)
257    unord_q,
258    /// Not-equal (unordered, non-signaling)
259    neq_uq,
260    /// Not-less-than (unordered, signaling)
261    nlt_us,
262    /// Not-less-than-or-equal (unordered, signaling)
263    nle_us,
264    /// Ordered (non-signaling)
265    ord_q,
266    /// Equal (unordered, non-signaling)
267    eq_uq,
268    /// Not-greater-than-or-equal (unordered, signaling)
269    nge_us,
270    /// Not-greater-than (unordered, signaling)
271    ngt_us,
272    /// False (ordered, non-signaling)
273    false_oq,
274    /// Not-equal (ordered, non-signaling)
275    neq_oq,
276    /// Greater-than-or-equal (ordered, signaling)
277    ge_os,
278    /// Greater-than (ordered, signaling)
279    gt_os,
280    /// True (unordered, non-signaling)
281    true_uq,
282    /// Equal (unordered, non-signaling)
283    eq_os,
284    /// Less-than (ordered, non-signaling)
285    lt_oq,
286    /// Less-than-or-equal (ordered, non-signaling)
287    le_oq,
288    /// Unordered (signaling)
289    unord_s,
290    /// Not-equal (unordered, signaling)
291    neq_us,
292    /// Not-less-than (unordered, non-signaling)
293    nlt_uq,
294    /// Not-less-than-or-equal (unordered, non-signaling)
295    nle_uq,
296    /// Ordered (signaling)
297    ord_s,
298    /// Equal (unordered, signaling)
299    eq_us,
300    /// Not-greater-than-or-equal (unordered, non-signaling)
301    nge_uq,
302    /// Not-greater-than (unordered, non-signaling)
303    ngt_uq,
304    /// False (ordered, signaling)
305    false_os,
306    /// Not-equal (ordered, signaling)
307    neq_os,
308    /// Greater-than-or-equal (ordered, non-signaling)
309    ge_oq,
310    /// Greater-than (ordered, non-signaling)
311    gt_oq,
312    /// True (unordered, signaling)
313    true_us,
314
315    /// Equal (ordered, non-signaling)
316    pub const eq: VexFloatPredicate = .eq_oq;
317    /// Less-than (ordered, signaling)
318    pub const lt: VexFloatPredicate = .lt_os;
319    /// Less-than-or-equal (ordered, signaling)
320    pub const le: VexFloatPredicate = .le_os;
321    /// Unordered (non-signaling)
322    pub const unord: VexFloatPredicate = .unord_q;
323    /// Not-equal (unordered, non-signaling)
324    pub const neq: VexFloatPredicate = .neq_uq;
325    /// Not-less-than (unordered, signaling)
326    pub const nlt: VexFloatPredicate = .nlt_us;
327    /// Not-less-than-or-equal (unordered, signaling)
328    pub const nle: VexFloatPredicate = .nle_us;
329    /// Ordered (non-signaling)
330    pub const ord: VexFloatPredicate = .ord_q;
331    /// Not-greater-than-or-equal (unordered, signaling)
332    pub const nge: VexFloatPredicate = .nge_us;
333    /// Not-greater-than (unordered, signaling)
334    pub const ngt: VexFloatPredicate = .ngt_us;
335    /// False (ordered, non-signaling)
336    pub const @"false": VexFloatPredicate = .false_oq;
337    /// Greater-than-or-equal (ordered, signaling)
338    pub const ge: VexFloatPredicate = .ge_os;
339    /// Greater-than (ordered, signaling)
340    pub const gt: VexFloatPredicate = .gt_os;
341    /// True (unordered, non-signaling)
342    pub const @"true": VexFloatPredicate = .true_uq;
343
344    pub fn imm(pred: VexFloatPredicate) Immediate {
345        return .u(@intFromEnum(pred));
346    }
347};
348
349pub const Register = enum(u8) {
350    // zig fmt: off
351    rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
352    r8, r9, r10, r11, r12, r13, r14, r15,
353
354    eax, ecx, edx, ebx, esp, ebp, esi, edi,
355    r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
356
357    ax, cx, dx, bx, sp, bp, si, di,
358    r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
359
360    al, cl, dl, bl, spl, bpl, sil, dil,
361    r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
362
363    ah, ch, dh, bh,
364
365    zmm0,  zmm1, zmm2,  zmm3,  zmm4,  zmm5,  zmm6,  zmm7,
366    zmm8,  zmm9, zmm10, zmm11, zmm12, zmm13, zmm14, zmm15,
367    zmm16, zmm17,zmm18, zmm19, zmm20, zmm21, zmm22, zmm23,
368    zmm24, zmm25,zmm26, zmm27, zmm28, zmm29, zmm30, zmm31,
369
370    ymm0,  ymm1, ymm2,  ymm3,  ymm4,  ymm5,  ymm6,  ymm7,
371    ymm8,  ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
372    ymm16, ymm17,ymm18, ymm19, ymm20, ymm21, ymm22, ymm23,
373    ymm24, ymm25,ymm26, ymm27, ymm28, ymm29, ymm30, ymm31,
374
375    xmm0,  xmm1, xmm2,  xmm3,  xmm4,  xmm5,  xmm6,  xmm7,
376    xmm8,  xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
377    xmm16, xmm17,xmm18, xmm19, xmm20, xmm21, xmm22, xmm23,
378    xmm24, xmm25,xmm26, xmm27, xmm28, xmm29, xmm30, xmm31,
379
380    mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
381
382    st0, st1, st2, st3, st4, st5, st6, st7,
383
384    es, cs, ss, ds, fs, gs,
385
386    rip, eip, ip,
387
388    cr0, cr1, cr2,  cr3,  cr4,  cr5,  cr6,  cr7,
389    cr8, cr9, cr10, cr11, cr12, cr13, cr14, cr15,
390
391    dr0, dr1, dr2,  dr3,  dr4,  dr5,  dr6,  dr7,
392    dr8, dr9, dr10, dr11, dr12, dr13, dr14, dr15,
393
394    none,
395    // zig fmt: on
396
397    pub const Class = enum {
398        general_purpose,
399        gphi,
400        segment,
401        x87,
402        mmx,
403        sse,
404        ip,
405        cr,
406        dr,
407    };
408
409    pub fn class(reg: Register) Class {
410        return switch (@intFromEnum(reg)) {
411            // zig fmt: off
412            @intFromEnum(Register.rax)  ... @intFromEnum(Register.r15)   => .general_purpose,
413            @intFromEnum(Register.eax)  ... @intFromEnum(Register.r15d)  => .general_purpose,
414            @intFromEnum(Register.ax)   ... @intFromEnum(Register.r15w)  => .general_purpose,
415            @intFromEnum(Register.al)   ... @intFromEnum(Register.r15b)  => .general_purpose,
416            @intFromEnum(Register.ah)   ... @intFromEnum(Register.bh)    => .gphi,
417
418            @intFromEnum(Register.zmm0) ... @intFromEnum(Register.zmm31) => .sse,
419            @intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm31) => .sse,
420            @intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm31) => .sse,
421            @intFromEnum(Register.mm0)  ... @intFromEnum(Register.mm7)   => .mmx,
422            @intFromEnum(Register.st0)  ... @intFromEnum(Register.st7)   => .x87,
423
424            @intFromEnum(Register.es)   ... @intFromEnum(Register.gs)    => .segment,
425            @intFromEnum(Register.rip)  ... @intFromEnum(Register.ip)    => .ip,
426            @intFromEnum(Register.cr0)  ... @intFromEnum(Register.cr15)  => .cr,
427            @intFromEnum(Register.dr0)  ... @intFromEnum(Register.dr15)  => .dr,
428
429            else => unreachable,
430            // zig fmt: on
431        };
432    }
433
434    pub inline fn isClass(reg: Register, rc: Class) bool {
435        switch (rc) {
436            else => return reg.class() == rc,
437            .gphi => {
438                const reg_id = reg.id();
439                return (reg_id >= comptime Register.ah.id()) and reg_id <= comptime Register.bh.id();
440            },
441        }
442    }
443
444    pub fn id(reg: Register) u7 {
445        const base = switch (@intFromEnum(reg)) {
446            // zig fmt: off
447            @intFromEnum(Register.rax)  ... @intFromEnum(Register.r15)   => @intFromEnum(Register.rax),
448            @intFromEnum(Register.eax)  ... @intFromEnum(Register.r15d)  => @intFromEnum(Register.eax),
449            @intFromEnum(Register.ax)   ... @intFromEnum(Register.r15w)  => @intFromEnum(Register.ax),
450            @intFromEnum(Register.al)   ... @intFromEnum(Register.r15b)  => @intFromEnum(Register.al),
451            @intFromEnum(Register.ah)   ... @intFromEnum(Register.bh)    => @intFromEnum(Register.ah),
452
453            @intFromEnum(Register.zmm0) ... @intFromEnum(Register.zmm31) => @intFromEnum(Register.zmm0) - 16,
454            @intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm31) => @intFromEnum(Register.ymm0) - 16,
455            @intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm31) => @intFromEnum(Register.xmm0) - 16,
456            @intFromEnum(Register.mm0)  ... @intFromEnum(Register.mm7)   => @intFromEnum(Register.mm0)  - 48,
457            @intFromEnum(Register.st0)  ... @intFromEnum(Register.st7)   => @intFromEnum(Register.st0)  - 56,
458            @intFromEnum(Register.es)   ... @intFromEnum(Register.gs)    => @intFromEnum(Register.es)   - 64,
459            @intFromEnum(Register.cr0)  ... @intFromEnum(Register.cr15)  => @intFromEnum(Register.cr0)  - 70,
460            @intFromEnum(Register.dr0)  ... @intFromEnum(Register.dr15)  => @intFromEnum(Register.dr0)  - 86,
461
462            else => unreachable,
463            // zig fmt: on
464        };
465        return @intCast(@intFromEnum(reg) - base);
466    }
467
468    pub fn size(reg: Register) Memory.Size {
469        return switch (@intFromEnum(reg)) {
470            // zig fmt: off
471            @intFromEnum(Register.rax)  ... @intFromEnum(Register.r15)   => .qword,
472            @intFromEnum(Register.eax)  ... @intFromEnum(Register.r15d)  => .dword,
473            @intFromEnum(Register.ax)   ... @intFromEnum(Register.r15w)  => .word,
474            @intFromEnum(Register.al)   ... @intFromEnum(Register.r15b)  => .byte,
475            @intFromEnum(Register.ah)   ... @intFromEnum(Register.bh)    => .byte,
476
477            @intFromEnum(Register.zmm0) ... @intFromEnum(Register.zmm15) => .zword,
478            @intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => .yword,
479            @intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => .xword,
480            @intFromEnum(Register.mm0)  ... @intFromEnum(Register.mm7)   => .qword,
481            @intFromEnum(Register.st0)  ... @intFromEnum(Register.st7)   => .tbyte,
482
483            @intFromEnum(Register.es)   ... @intFromEnum(Register.gs)    => .word,
484
485            @intFromEnum(Register.cr0)  ... @intFromEnum(Register.cr15)  => .gpr,
486            @intFromEnum(Register.dr0)  ... @intFromEnum(Register.dr15)  => .gpr,
487
488            else => unreachable,
489            // zig fmt: on
490        };
491    }
492
493    pub fn isExtended(reg: Register) bool {
494        return switch (@intFromEnum(reg)) {
495            // zig fmt: off
496            @intFromEnum(Register.r8)  ... @intFromEnum(Register.r15)    => true,
497            @intFromEnum(Register.r8d) ... @intFromEnum(Register.r15d)   => true,
498            @intFromEnum(Register.r8w) ... @intFromEnum(Register.r15w)   => true,
499            @intFromEnum(Register.r8b) ... @intFromEnum(Register.r15b)   => true,
500
501            @intFromEnum(Register.zmm8) ... @intFromEnum(Register.zmm31) => true,
502            @intFromEnum(Register.ymm8) ... @intFromEnum(Register.ymm31) => true,
503            @intFromEnum(Register.xmm8) ... @intFromEnum(Register.xmm31) => true,
504
505            @intFromEnum(Register.cr8)  ... @intFromEnum(Register.cr15)  => true,
506            @intFromEnum(Register.dr8)  ... @intFromEnum(Register.dr15)  => true,
507
508            else => false,
509            // zig fmt: on
510        };
511    }
512
513    pub fn enc(reg: Register) u5 {
514        const base = switch (@intFromEnum(reg)) {
515            // zig fmt: off
516            @intFromEnum(Register.rax)  ... @intFromEnum(Register.r15)   => @intFromEnum(Register.rax),
517            @intFromEnum(Register.eax)  ... @intFromEnum(Register.r15d)  => @intFromEnum(Register.eax),
518            @intFromEnum(Register.ax)   ... @intFromEnum(Register.r15w)  => @intFromEnum(Register.ax),
519            @intFromEnum(Register.al)   ... @intFromEnum(Register.r15b)  => @intFromEnum(Register.al),
520            @intFromEnum(Register.ah)   ... @intFromEnum(Register.bh)    => @intFromEnum(Register.ah) - 4,
521
522            @intFromEnum(Register.ymm0) ... @intFromEnum(Register.ymm15) => @intFromEnum(Register.ymm0),
523            @intFromEnum(Register.xmm0) ... @intFromEnum(Register.xmm15) => @intFromEnum(Register.xmm0),
524            @intFromEnum(Register.mm0)  ... @intFromEnum(Register.mm7)   => @intFromEnum(Register.mm0),
525            @intFromEnum(Register.st0)  ... @intFromEnum(Register.st7)   => @intFromEnum(Register.st0),
526
527            @intFromEnum(Register.es)   ... @intFromEnum(Register.gs)    => @intFromEnum(Register.es),
528
529            @intFromEnum(Register.cr0)  ... @intFromEnum(Register.cr15)  => @intFromEnum(Register.cr0),
530            @intFromEnum(Register.dr0)  ... @intFromEnum(Register.dr15)  => @intFromEnum(Register.dr0),
531
532            else => unreachable,
533            // zig fmt: on
534        };
535        return @truncate(@intFromEnum(reg) - base);
536    }
537
538    pub fn toBitSize(reg: Register, bit_size: u64) Register {
539        return switch (bit_size) {
540            8 => reg.to8(),
541            16 => reg.to16(),
542            32 => reg.to32(),
543            64 => reg.to64(),
544            80 => reg.to80(),
545            128 => reg.to128(),
546            256 => reg.to256(),
547            512 => reg.to512(),
548            else => unreachable,
549        };
550    }
551
552    pub fn toSize(reg: Register, new_size: Memory.Size, target: *const std.Target) Register {
553        return switch (new_size) {
554            .none => unreachable,
555            .ptr => reg.toBitSize(target.ptrBitWidth()),
556            .gpr => switch (target.cpu.arch) {
557                else => unreachable,
558                .x86 => reg.to32(),
559                .x86_64 => reg.to64(),
560            },
561            .low_byte => reg.toLo8(),
562            .high_byte => reg.toHi8(),
563            .byte => reg.to8(),
564            .word => reg.to16(),
565            .dword => reg.to32(),
566            .qword => reg.to64(),
567            .tbyte => reg.to80(),
568            .xword => reg.to128(),
569            .yword => reg.to256(),
570            .zword => reg.to512(),
571        };
572    }
573
574    fn gpBase(reg: Register) u7 {
575        return switch (@intFromEnum(reg)) {
576            // zig fmt: off
577            @intFromEnum(Register.rax)  ... @intFromEnum(Register.r15)   => @intFromEnum(Register.rax),
578            @intFromEnum(Register.eax)  ... @intFromEnum(Register.r15d)  => @intFromEnum(Register.eax),
579            @intFromEnum(Register.ax)   ... @intFromEnum(Register.r15w)  => @intFromEnum(Register.ax),
580            @intFromEnum(Register.al)   ... @intFromEnum(Register.r15b)  => @intFromEnum(Register.al),
581            @intFromEnum(Register.ah)   ... @intFromEnum(Register.bh)    => @intFromEnum(Register.ah),
582            else => unreachable,
583            // zig fmt: on
584        };
585    }
586
587    pub fn to64(reg: Register) Register {
588        return switch (reg.class()) {
589            .general_purpose, .gphi => @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.rax)),
590            .segment => unreachable,
591            .x87, .mmx, .cr, .dr => reg,
592            .sse => reg.to128(),
593            .ip => .rip,
594        };
595    }
596
597    pub fn to32(reg: Register) Register {
598        return switch (reg.class()) {
599            .general_purpose, .gphi => @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.eax)),
600            .segment => unreachable,
601            .x87, .mmx, .cr, .dr => reg,
602            .sse => reg.to128(),
603            .ip => .eip,
604        };
605    }
606
607    pub fn to16(reg: Register) Register {
608        return switch (reg.class()) {
609            .general_purpose, .gphi => @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.ax)),
610            .segment, .x87, .mmx, .cr, .dr => reg,
611            .sse => reg.to128(),
612            .ip => .ip,
613        };
614    }
615
616    pub fn to8(reg: Register) Register {
617        return switch (reg.class()) {
618            .general_purpose => reg.toLo8(),
619            .gphi, .segment, .x87, .mmx, .cr, .dr => reg,
620            .sse => reg.to128(),
621            .ip => .ip,
622        };
623    }
624
625    pub fn toLo8(reg: Register) Register {
626        return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.al));
627    }
628
629    pub fn toHi8(reg: Register) Register {
630        assert(reg.isClass(.gphi));
631        return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.ah));
632    }
633
634    pub fn to80(reg: Register) Register {
635        assert(reg.isClass(.x87));
636        return reg;
637    }
638
639    fn sseBase(reg: Register) u8 {
640        assert(reg.isClass(.sse));
641        return switch (@intFromEnum(reg)) {
642            @intFromEnum(Register.zmm0)...@intFromEnum(Register.zmm31) => @intFromEnum(Register.zmm0),
643            @intFromEnum(Register.ymm0)...@intFromEnum(Register.ymm31) => @intFromEnum(Register.ymm0),
644            @intFromEnum(Register.xmm0)...@intFromEnum(Register.xmm31) => @intFromEnum(Register.xmm0),
645            else => unreachable,
646        };
647    }
648
649    pub fn to512(reg: Register) Register {
650        return @enumFromInt(@intFromEnum(reg) - reg.sseBase() + @intFromEnum(Register.zmm0));
651    }
652
653    pub fn to256(reg: Register) Register {
654        return @enumFromInt(@intFromEnum(reg) - reg.sseBase() + @intFromEnum(Register.ymm0));
655    }
656
657    pub fn to128(reg: Register) Register {
658        return @enumFromInt(@intFromEnum(reg) - reg.sseBase() + @intFromEnum(Register.xmm0));
659    }
660
661    /// DWARF register encoding
662    pub fn dwarfNum(reg: Register) u6 {
663        return switch (reg.class()) {
664            .general_purpose, .gphi => if (reg.isExtended())
665                reg.enc()
666            else
667                @as(u3, @truncate(@as(u24, 0o54673120) >> @as(u5, reg.enc()) * 3)),
668            .sse => 17 + @as(u6, reg.enc()),
669            .x87 => 33 + @as(u6, reg.enc()),
670            .mmx => 41 + @as(u6, reg.enc()),
671            .segment => 50 + @as(u6, reg.enc()),
672            .ip => 16,
673            .cr, .dr => unreachable,
674        };
675    }
676};
677
678test "Register id - different classes" {
679    try expect(Register.al.id() == Register.ax.id());
680    try expect(Register.ah.id() != Register.spl.id());
681    try expect(Register.ax.id() == Register.eax.id());
682    try expect(Register.eax.id() == Register.rax.id());
683
684    try expect(Register.ymm0.id() == 0b10000);
685    try expect(Register.ymm0.id() != Register.rax.id());
686    try expect(Register.xmm0.id() == Register.ymm0.id());
687    try expect(Register.xmm0.id() != Register.mm0.id());
688    try expect(Register.mm0.id() != Register.st0.id());
689}
690
691test "Register enc - different classes" {
692    try expect(Register.al.enc() == Register.ax.enc());
693    try expect(Register.ah.enc() == Register.spl.enc());
694    try expect(Register.ax.enc() == Register.eax.enc());
695    try expect(Register.eax.enc() == Register.rax.enc());
696    try expect(Register.ymm0.enc() == Register.rax.enc());
697    try expect(Register.xmm0.enc() == Register.ymm0.enc());
698    try expect(Register.es.enc() == Register.rax.enc());
699}
700
701test "Register classes" {
702    try expect(Register.r11.isClass(.general_purpose));
703    try expect(Register.rdx.isClass(.gphi));
704    try expect(!Register.dil.isClass(.gphi));
705    try expect(Register.ymm11.isClass(.sse));
706    try expect(Register.mm3.isClass(.mmx));
707    try expect(Register.st3.isClass(.x87));
708    try expect(Register.fs.isClass(.segment));
709}
710
711pub const FrameIndex = enum(u32) {
712    // This index refers to the start of the arguments passed to this function
713    args_frame,
714    // This index refers to the return address pushed by a `call` and popped by a `ret`.
715    ret_addr,
716    // This index refers to the base pointer pushed in the prologue and popped in the epilogue.
717    base_ptr,
718    // This index refers to the entire stack frame.
719    stack_frame,
720    // This index refers to the start of the call frame for arguments passed to called functions
721    call_frame,
722    // Other indices are used for local variable stack slots
723    _,
724
725    pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len;
726
727    pub fn isNamed(fi: FrameIndex) bool {
728        return @intFromEnum(fi) < named_count;
729    }
730
731    pub fn format(fi: FrameIndex, writer: *std.Io.Writer) std.Io.Writer.Error!void {
732        if (fi.isNamed()) {
733            try writer.print("FrameIndex.{t}", .{fi});
734        } else {
735            try writer.print("FrameIndex({d})", .{@intFromEnum(fi)});
736        }
737    }
738};
739
740pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
741
742pub const RegisterOffset = struct { reg: Register, off: i32 = 0 };
743
744pub const NavOffset = struct { index: InternPool.Nav.Index, off: i32 = 0 };
745
746pub const Memory = struct {
747    base: Base = .none,
748    mod: Mod = .{ .rm = .{} },
749
750    pub const Base = union(enum(u4)) {
751        none,
752        reg: Register,
753        frame: FrameIndex,
754        table,
755        rip_inst: Mir.Inst.Index,
756        nav: InternPool.Nav.Index,
757        uav: InternPool.Key.Ptr.BaseAddr.Uav,
758        lazy_sym: link.File.LazySymbol,
759        extern_func: Mir.NullTerminatedString,
760
761        pub const Tag = @typeInfo(Base).@"union".tag_type.?;
762    };
763
764    pub const Mod = union(enum(u1)) {
765        rm: Rm,
766        off: u64,
767
768        pub const Rm = struct {
769            size: Size = .none,
770            index: Register = .none,
771            scale: Scale = .@"1",
772            disp: i32 = 0,
773        };
774    };
775
776    pub const Size = enum(u4) {
777        none,
778        ptr,
779        gpr,
780        low_byte,
781        high_byte,
782        byte,
783        word,
784        dword,
785        qword,
786        tbyte,
787        xword,
788        yword,
789        zword,
790
791        pub fn fromSize(size: u32) Size {
792            return switch (size) {
793                1...1 => .byte,
794                2...2 => .word,
795                3...4 => .dword,
796                5...8 => .qword,
797                9...16 => .xword,
798                17...32 => .yword,
799                33...64 => .zword,
800                else => unreachable,
801            };
802        }
803
804        pub fn fromBitSize(bit_size: u64) Size {
805            return switch (bit_size) {
806                8 => .byte,
807                16 => .word,
808                32 => .dword,
809                64 => .qword,
810                80 => .tbyte,
811                128 => .xword,
812                256 => .yword,
813                512 => .zword,
814                else => unreachable,
815            };
816        }
817
818        pub fn bitSize(s: Size, target: *const std.Target) u64 {
819            return switch (s) {
820                .none => 0,
821                .ptr => target.ptrBitWidth(),
822                .gpr => switch (target.cpu.arch) {
823                    else => unreachable,
824                    .x86 => 32,
825                    .x86_64 => 64,
826                },
827                .low_byte, .high_byte, .byte => 8,
828                .word => 16,
829                .dword => 32,
830                .qword => 64,
831                .tbyte => 80,
832                .xword => 128,
833                .yword => 256,
834                .zword => 512,
835            };
836        }
837
838        pub fn format(s: Size, writer: *std.Io.Writer) std.Io.Writer.Error!void {
839            if (s == .none) return;
840            try writer.writeAll(@tagName(s));
841            switch (s) {
842                .none => unreachable,
843                .ptr, .gpr => {},
844                else => {
845                    try writer.writeByte(' ');
846                    try writer.writeAll("ptr");
847                },
848            }
849        }
850    };
851
852    pub const Scale = enum(u2) {
853        @"1",
854        @"2",
855        @"4",
856        @"8",
857
858        pub fn fromFactor(factor: u4) Scale {
859            return switch (factor) {
860                else => unreachable,
861                1 => .@"1",
862                2 => .@"2",
863                4 => .@"4",
864                8 => .@"8",
865            };
866        }
867
868        pub fn toFactor(scale: Scale) u4 {
869            return switch (scale) {
870                .@"1" => 1,
871                .@"2" => 2,
872                .@"4" => 4,
873                .@"8" => 8,
874            };
875        }
876
877        pub fn fromLog2(log2: u2) Scale {
878            return @enumFromInt(log2);
879        }
880
881        pub fn toLog2(scale: Scale) u2 {
882            return @intFromEnum(scale);
883        }
884    };
885};
886
887pub const Immediate = union(enum) {
888    signed: i32,
889    unsigned: u64,
890    nav: NavOffset,
891    uav: InternPool.Key.Ptr.BaseAddr.Uav,
892    lazy_sym: link.File.LazySymbol,
893    extern_func: Mir.NullTerminatedString,
894
895    pub fn u(x: u64) Immediate {
896        return .{ .unsigned = x };
897    }
898
899    pub fn s(x: i32) Immediate {
900        return .{ .signed = x };
901    }
902
903    pub fn format(imm: Immediate, writer: *std.Io.Writer) std.Io.Writer.Error!void {
904        switch (imm) {
905            inline else => |int| try writer.print("{d}", .{int}),
906            .nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }),
907            .uav => |uav| try writer.print("Uav({d})", .{@intFromEnum(uav.val)}),
908            .lazy_sym => |lazy_sym| try writer.print("LazySym({s}, {d})", .{ @tagName(lazy_sym.kind), @intFromEnum(lazy_sym.ty) }),
909            .extern_func => |extern_func| try writer.print("ExternFunc({d})", .{@intFromEnum(extern_func)}),
910        }
911    }
912};