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};