rucc-target 0.3.10

Target descriptions, register files and ABI data for the rucc C compiler.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
//! What each x86-64 machine instruction is in assembly.
//!
//! Design: `spec/11-asm-objects-debug.md` section 11.1.
//!
//! The compiler writes bytes rather than text, and `-S` writes text because people read it. Both
//! read this, which is what section 11.1 asks for: an assembly listing that disagrees with the
//! object file next to it is worse than no listing at all, and the only way to be sure they agree
//! is for there to be one description and not two.
//!
//! An opcode in the machine IR is one instruction to the allocator and is not always one
//! instruction to the machine, which [`Form`](crate::x86_64::Form) already says. So what is
//! written for an opcode is a list of them: a comparison is a `cmp` and a `set`, an unsigned
//! division is the clearing of the high half and then the division, and the three opcodes that
//! exist to hold a value in a register until something reads it are written as nothing at all.
//!
//! # The order the arguments are in
//!
//! AT&T syntax, which puts the source before the destination and is the reverse of the order the
//! operand vector holds them in. That is why an argument names the operand it wants by index
//! rather than the arguments being the operand vector: the two orders are different, an
//! instruction may name one operand twice, and an instruction the machine writes in the middle of
//! an opcode may name none of them.
//!
//! Intel syntax is the other order and `spec/11-asm-objects-debug.md` section 11.1 requires it as
//! an input. Writing it is a second pass over this table rather than a second table, since the
//! difference is the order of the arguments and the spelling of a memory operand, and neither is
//! a different instruction.
//!
//! # The width
//!
//! A register is one register at every width and `al`, `ax`, `eax` and `rax` are four ways of
//! writing part of `rax`, which is why the register file names only the last of them. The width
//! belongs to the instruction, so it is written on the argument, and [`gpr_name`] is where the
//! two are put together.
//!
//! It is on the argument rather than on the instruction because the two are not always the same.
//! A comparison of two sixty four bit registers sets a byte, a shift of a sixty four bit register
//! reads its count from a byte, and an eight bit multiply is done thirty two bits at a time
//! because the machine has no two-operand multiply narrower than that and the low eight bits of a
//! product depend on nothing but the low eight bits of what went into it.

use crate::regs::PhysReg;

use Arg::{Imm, Label, Mem, Named, Reg, Symbol, Through, Xmm};
use Width::{Byte, Long, Quad, Word};

/// How much of a register one argument of one instruction is.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Width {
    /// Eight bits, which is `al`.
    Byte,
    /// Sixteen bits, `ax`.
    Word,
    /// Thirty two bits, `eax`.
    Long,
    /// Sixty four bits, `rax`, and the spelling of any register whose class has only one.
    Quad,
}

impl Width {
    /// Which of the four spellings of a register this is, counting from the narrowest.
    #[must_use]
    pub fn index(self) -> usize {
        match self {
            Byte => 0,
            Word => 1,
            Long => 2,
            Quad => 3,
        }
    }
}

/// One argument of one assembly instruction.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arg {
    /// The register of the operand at that index in the operand vector, that much of it.
    Reg(u8, Width),
    /// The vector register of the operand at that index, all of it.
    ///
    /// The same thing as [`Arg::Reg`] in the other register file, and a variant of its own rather
    /// than a width, because which file a register is in is what tells two instructions apart:
    /// `movq %rax, %rbx` and `movq %xmm0, %rax` are the same mnemonic with two register arguments
    /// and are different instructions, and nothing else about either of them says so. The text
    /// path never needed the answer, since the class is on the operand and that is what a register
    /// is named from, and the byte path has no operand to ask.
    ///
    /// No width. Every class on this machine but the general purpose one has a single name per
    /// register, which is what the register file holds, so there is nothing for a width to pick
    /// between.
    Xmm(u8),
    /// A register the instruction names itself, which is one no operand could name.
    ///
    /// There is one, which is `ah`. It is the high byte of the remainder an eight bit division
    /// gives back, and it is not an operand because a program that names it cannot also name
    /// `sil` or `r8b` in the same instruction, so an allocator that could put a value there would
    /// have to know which other registers the instruction had been given.
    Named(&'static str),
    /// The immediate the instruction carries.
    Imm,
    /// The addressing mode the instruction carries.
    Mem,
    /// The symbol the instruction names, which is what a call goes to.
    Symbol,
    /// The register a call goes through, which the assembler writes with a star in front of it.
    ///
    /// The star is what tells the two calls apart in AT&T syntax. `call f` goes to the place the
    /// name is at and `call *%rax` goes to the place the register holds, and without it a call
    /// through an address would be written as a call to whatever the register happened to be
    /// called. It is on the argument rather than in the mnemonic because it is a fact about the
    /// argument: it says the operand is where the target is, not that the target is there.
    ///
    /// The first operand the instruction reads, rather than the operand at an index, which is the
    /// one place in this table an argument is named that way. A call writes the register the
    /// value comes back in and every register the callee may destroy before it reads anything,
    /// and how many of those there are depends on the signature and the convention, so no index
    /// written here would be the same from one call to the next. Where the address is put is
    /// `rucc_codegen::abi`'s answer, which is in front of the arguments.
    ///
    /// A whole register, because an address is one. There is no width to write.
    Through,
    /// The block the instruction goes to, which is the first successor of the block it ends.
    Label,
}

/// One instruction of the machine, as an assembler reads it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Written {
    /// The mnemonic, with the letter that says how wide it is already on it.
    pub mnemonic: &'static str,
    /// Its arguments, in the order they are written.
    pub args: &'static [Arg],
}

/// One instruction of the machine, for the table below.
const fn spell(mnemonic: &'static str, args: &'static [Arg]) -> Written {
    Written { mnemonic, args }
}

// The comparison in front of a set, which reads its second source and then its first. The
// destination is not one of its arguments, because what a comparison writes is the flags and what
// the byte at index zero gets is written by the set behind it.
static CMP_8: [Arg; 2] = [Reg(2, Byte), Reg(1, Byte)];
static CMP_16: [Arg; 2] = [Reg(2, Word), Reg(1, Word)];
static CMP_32: [Arg; 2] = [Reg(2, Long), Reg(1, Long)];
static CMP_64: [Arg; 2] = [Reg(2, Quad), Reg(1, Quad)];
// What the set writes, which is a byte whatever was compared to produce it.
static SET: [Arg; 1] = [Reg(0, Byte)];

// The same for two floats, which is the same shape one operand further along when the opcode
// carries the spare byte as a second destination.
static UCOMI: [Arg; 2] = [Xmm(2), Xmm(1)];
static UCOMI_BOTH: [Arg; 2] = [Xmm(3), Xmm(2)];
// The spare byte the two conditions that take two sets write, and the operation that puts the two
// bytes together into the one at index zero.
static SET_SPARE: [Arg; 1] = [Reg(1, Byte)];
static COMBINE: [Arg; 2] = [Reg(1, Byte), Reg(0, Byte)];

// The divisor, which is the one register a division names. Everything else it touches is a fixed
// register the opcode carries as an operand so that the allocator keeps out of it.
static DIVISOR_8: [Arg; 1] = [Reg(3, Byte)];
static DIVISOR_16: [Arg; 1] = [Reg(3, Word)];
static DIVISOR_32: [Arg; 1] = [Reg(3, Long)];
static DIVISOR_64: [Arg; 1] = [Reg(3, Quad)];

// An unsigned division divides a pair of registers by one, so the high half of the pair has to be
// zero before it runs, and a signed one sign extends into it instead, which is what `cltd` and the
// three like it are for. Which operand the high half is depends on which answer the opcode gives
// back: the quotient's opcode carries it as the clobber at index one and the remainder's opcode
// carries it as the destination at index zero, and both are `rdx`.
static CLEAR_FOR_QUOTIENT: [Arg; 2] = [Reg(1, Long), Reg(1, Long)];
static CLEAR_FOR_REMAINDER: [Arg; 2] = [Reg(0, Long), Reg(0, Long)];

// An eight bit division is the one that is not a pair of registers. The dividend is the whole of
// `ax`, the quotient comes back in `al` and the remainder in `ah`, so the dividend is widened into
// `ax` first and the remainder is moved down out of `ah` afterwards. The widening is written here
// only for the unsigned pair, because the signed pair has an instruction of its own for it.
static WIDEN_FOR_QUOTIENT: [Arg; 2] = [Reg(2, Byte), Reg(0, Long)];
static WIDEN_FOR_REMAINDER: [Arg; 2] = [Reg(2, Byte), Reg(1, Long)];
static HIGH_HALF: [Arg; 2] = [Named("ah"), Reg(0, Byte)];

/// Every x86-64 opcode, and the instructions the machine writes for it.
///
/// In the same order as [`INSTS`](crate::x86_64::INSTS) and grouped the same way, because the two
/// are read together and a name that is in one and not the other is a mistake either way round.
///
/// An empty list is an opcode that is not an instruction. Three of them exist to say where a
/// value already is or has to be, which is a fact the allocator needs and the machine does not,
/// and a fourth is the condition a block leaves on, which the block layout takes back out and
/// replaces with a test and a jump.
static TEXT: &[(&str, &[Written])] = &[
    // Constants. The sixty four bit form is written as an ordinary move and the assembler is the
    // one that reaches for the ten byte encoding when the number does not fit in four.
    ("mov_ri_8", &[spell("movb", &[Imm, Reg(0, Byte)])]),
    ("mov_ri_16", &[spell("movw", &[Imm, Reg(0, Word)])]),
    ("mov_ri_32", &[spell("movl", &[Imm, Reg(0, Long)])]),
    ("mov_ri_64", &[spell("movq", &[Imm, Reg(0, Quad)])]),
    // Arithmetic, register with register. The destination is the first source, which the allocator
    // has arranged by the time any of this is written, so the first source is not an argument.
    ("add_rr_8", &[spell("addb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("add_rr_16", &[spell("addw", &[Reg(2, Word), Reg(0, Word)])]),
    ("add_rr_32", &[spell("addl", &[Reg(2, Long), Reg(0, Long)])]),
    ("add_rr_64", &[spell("addq", &[Reg(2, Quad), Reg(0, Quad)])]),
    ("sub_rr_8", &[spell("subb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("sub_rr_16", &[spell("subw", &[Reg(2, Word), Reg(0, Word)])]),
    ("sub_rr_32", &[spell("subl", &[Reg(2, Long), Reg(0, Long)])]),
    ("sub_rr_64", &[spell("subq", &[Reg(2, Quad), Reg(0, Quad)])]),
    ("and_rr_8", &[spell("andb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("and_rr_16", &[spell("andw", &[Reg(2, Word), Reg(0, Word)])]),
    ("and_rr_32", &[spell("andl", &[Reg(2, Long), Reg(0, Long)])]),
    ("and_rr_64", &[spell("andq", &[Reg(2, Quad), Reg(0, Quad)])]),
    ("or_rr_8", &[spell("orb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("or_rr_16", &[spell("orw", &[Reg(2, Word), Reg(0, Word)])]),
    ("or_rr_32", &[spell("orl", &[Reg(2, Long), Reg(0, Long)])]),
    ("or_rr_64", &[spell("orq", &[Reg(2, Quad), Reg(0, Quad)])]),
    ("xor_rr_8", &[spell("xorb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("xor_rr_16", &[spell("xorw", &[Reg(2, Word), Reg(0, Word)])]),
    ("xor_rr_32", &[spell("xorl", &[Reg(2, Long), Reg(0, Long)])]),
    ("xor_rr_64", &[spell("xorq", &[Reg(2, Quad), Reg(0, Quad)])]),
    // The eight bit multiply is done thirty two bits at a time, because the machine has no
    // two-operand multiply narrower than sixteen and the low eight bits of a product are decided
    // by the low eight bits of what went into it. Whatever ends up above them is not part of an
    // eight bit value and nothing that reads one looks there.
    ("imul_rr_8", &[spell("imull", &[Reg(2, Long), Reg(0, Long)])]),
    ("imul_rr_16", &[spell("imulw", &[Reg(2, Word), Reg(0, Word)])]),
    ("imul_rr_32", &[spell("imull", &[Reg(2, Long), Reg(0, Long)])]),
    ("imul_rr_64", &[spell("imulq", &[Reg(2, Quad), Reg(0, Quad)])]),
    // Arithmetic, register with immediate.
    ("add_ri_8", &[spell("addb", &[Imm, Reg(0, Byte)])]),
    ("add_ri_16", &[spell("addw", &[Imm, Reg(0, Word)])]),
    ("add_ri_32", &[spell("addl", &[Imm, Reg(0, Long)])]),
    ("add_ri_64", &[spell("addq", &[Imm, Reg(0, Quad)])]),
    ("sub_ri_8", &[spell("subb", &[Imm, Reg(0, Byte)])]),
    ("sub_ri_16", &[spell("subw", &[Imm, Reg(0, Word)])]),
    ("sub_ri_32", &[spell("subl", &[Imm, Reg(0, Long)])]),
    ("sub_ri_64", &[spell("subq", &[Imm, Reg(0, Quad)])]),
    ("and_ri_8", &[spell("andb", &[Imm, Reg(0, Byte)])]),
    ("and_ri_16", &[spell("andw", &[Imm, Reg(0, Word)])]),
    ("and_ri_32", &[spell("andl", &[Imm, Reg(0, Long)])]),
    ("and_ri_64", &[spell("andq", &[Imm, Reg(0, Quad)])]),
    ("or_ri_8", &[spell("orb", &[Imm, Reg(0, Byte)])]),
    ("or_ri_16", &[spell("orw", &[Imm, Reg(0, Word)])]),
    ("or_ri_32", &[spell("orl", &[Imm, Reg(0, Long)])]),
    ("or_ri_64", &[spell("orq", &[Imm, Reg(0, Quad)])]),
    ("xor_ri_8", &[spell("xorb", &[Imm, Reg(0, Byte)])]),
    ("xor_ri_16", &[spell("xorw", &[Imm, Reg(0, Word)])]),
    ("xor_ri_32", &[spell("xorl", &[Imm, Reg(0, Long)])]),
    ("xor_ri_64", &[spell("xorq", &[Imm, Reg(0, Quad)])]),
    // A multiply by a constant is the one three-operand instruction on this machine, so its
    // source is written even though the destination is tied to it and they are the same register.
    ("imul_ri_8", &[spell("imull", &[Imm, Reg(1, Long), Reg(0, Long)])]),
    ("imul_ri_16", &[spell("imulw", &[Imm, Reg(1, Word), Reg(0, Word)])]),
    ("imul_ri_32", &[spell("imull", &[Imm, Reg(1, Long), Reg(0, Long)])]),
    ("imul_ri_64", &[spell("imulq", &[Imm, Reg(1, Quad), Reg(0, Quad)])]),
    // Negation and complement.
    ("neg_r_8", &[spell("negb", &[Reg(0, Byte)])]),
    ("neg_r_16", &[spell("negw", &[Reg(0, Word)])]),
    ("neg_r_32", &[spell("negl", &[Reg(0, Long)])]),
    ("neg_r_64", &[spell("negq", &[Reg(0, Quad)])]),
    ("not_r_8", &[spell("notb", &[Reg(0, Byte)])]),
    ("not_r_16", &[spell("notw", &[Reg(0, Word)])]),
    ("not_r_32", &[spell("notl", &[Reg(0, Long)])]),
    ("not_r_64", &[spell("notq", &[Reg(0, Quad)])]),
    // Division and remainder, signed and unsigned. The four widening instructions have no operands
    // at all: each of them reads one fixed register and writes another, and the opcode carries
    // both of those as operands so that the allocator leaves them alone.
    ("idiv_quo_8", &[spell("cbtw", &[]), spell("idivb", &DIVISOR_8)]),
    ("idiv_quo_16", &[spell("cwtd", &[]), spell("idivw", &DIVISOR_16)]),
    ("idiv_quo_32", &[spell("cltd", &[]), spell("idivl", &DIVISOR_32)]),
    ("idiv_quo_64", &[spell("cqto", &[]), spell("idivq", &DIVISOR_64)]),
    ("idiv_rem_8", &[spell("cbtw", &[]), spell("idivb", &DIVISOR_8), spell("movb", &HIGH_HALF)]),
    ("idiv_rem_16", &[spell("cwtd", &[]), spell("idivw", &DIVISOR_16)]),
    ("idiv_rem_32", &[spell("cltd", &[]), spell("idivl", &DIVISOR_32)]),
    ("idiv_rem_64", &[spell("cqto", &[]), spell("idivq", &DIVISOR_64)]),
    ("div_quo_8", &[spell("movzbl", &WIDEN_FOR_QUOTIENT), spell("divb", &DIVISOR_8)]),
    ("div_quo_16", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divw", &DIVISOR_16)]),
    ("div_quo_32", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divl", &DIVISOR_32)]),
    ("div_quo_64", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divq", &DIVISOR_64)]),
    (
        "div_rem_8",
        &[
            spell("movzbl", &WIDEN_FOR_REMAINDER),
            spell("divb", &DIVISOR_8),
            spell("movb", &HIGH_HALF),
        ],
    ),
    ("div_rem_16", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divw", &DIVISOR_16)]),
    ("div_rem_32", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divl", &DIVISOR_32)]),
    ("div_rem_64", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divq", &DIVISOR_64)]),
    // Shifts by a constant.
    ("shl_ri_8", &[spell("shlb", &[Imm, Reg(0, Byte)])]),
    ("shl_ri_16", &[spell("shlw", &[Imm, Reg(0, Word)])]),
    ("shl_ri_32", &[spell("shll", &[Imm, Reg(0, Long)])]),
    ("shl_ri_64", &[spell("shlq", &[Imm, Reg(0, Quad)])]),
    ("shr_ri_8", &[spell("shrb", &[Imm, Reg(0, Byte)])]),
    ("shr_ri_16", &[spell("shrw", &[Imm, Reg(0, Word)])]),
    ("shr_ri_32", &[spell("shrl", &[Imm, Reg(0, Long)])]),
    ("shr_ri_64", &[spell("shrq", &[Imm, Reg(0, Quad)])]),
    ("sar_ri_8", &[spell("sarb", &[Imm, Reg(0, Byte)])]),
    ("sar_ri_16", &[spell("sarw", &[Imm, Reg(0, Word)])]),
    ("sar_ri_32", &[spell("sarl", &[Imm, Reg(0, Long)])]),
    ("sar_ri_64", &[spell("sarq", &[Imm, Reg(0, Quad)])]),
    // Shifts by a register, which is `cl` and nothing else, so the count is a byte however wide
    // the thing being shifted is.
    ("shl_rcl_8", &[spell("shlb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("shl_rcl_16", &[spell("shlw", &[Reg(2, Byte), Reg(0, Word)])]),
    ("shl_rcl_32", &[spell("shll", &[Reg(2, Byte), Reg(0, Long)])]),
    ("shl_rcl_64", &[spell("shlq", &[Reg(2, Byte), Reg(0, Quad)])]),
    ("shr_rcl_8", &[spell("shrb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("shr_rcl_16", &[spell("shrw", &[Reg(2, Byte), Reg(0, Word)])]),
    ("shr_rcl_32", &[spell("shrl", &[Reg(2, Byte), Reg(0, Long)])]),
    ("shr_rcl_64", &[spell("shrq", &[Reg(2, Byte), Reg(0, Quad)])]),
    ("sar_rcl_8", &[spell("sarb", &[Reg(2, Byte), Reg(0, Byte)])]),
    ("sar_rcl_16", &[spell("sarw", &[Reg(2, Byte), Reg(0, Word)])]),
    ("sar_rcl_32", &[spell("sarl", &[Reg(2, Byte), Reg(0, Long)])]),
    ("sar_rcl_64", &[spell("sarq", &[Reg(2, Byte), Reg(0, Quad)])]),
    // The comparisons, ten conditions at four widths, each of them a comparison and the byte it
    // sets. The condition is in the second of the two and the width is in the first, which is why
    // neither of them alone is the instruction.
    ("cmp_set_e_8", &[spell("cmpb", &CMP_8), spell("sete", &SET)]),
    ("cmp_set_e_16", &[spell("cmpw", &CMP_16), spell("sete", &SET)]),
    ("cmp_set_e_32", &[spell("cmpl", &CMP_32), spell("sete", &SET)]),
    ("cmp_set_e_64", &[spell("cmpq", &CMP_64), spell("sete", &SET)]),
    ("cmp_set_ne_8", &[spell("cmpb", &CMP_8), spell("setne", &SET)]),
    ("cmp_set_ne_16", &[spell("cmpw", &CMP_16), spell("setne", &SET)]),
    ("cmp_set_ne_32", &[spell("cmpl", &CMP_32), spell("setne", &SET)]),
    ("cmp_set_ne_64", &[spell("cmpq", &CMP_64), spell("setne", &SET)]),
    ("cmp_set_l_8", &[spell("cmpb", &CMP_8), spell("setl", &SET)]),
    ("cmp_set_l_16", &[spell("cmpw", &CMP_16), spell("setl", &SET)]),
    ("cmp_set_l_32", &[spell("cmpl", &CMP_32), spell("setl", &SET)]),
    ("cmp_set_l_64", &[spell("cmpq", &CMP_64), spell("setl", &SET)]),
    ("cmp_set_le_8", &[spell("cmpb", &CMP_8), spell("setle", &SET)]),
    ("cmp_set_le_16", &[spell("cmpw", &CMP_16), spell("setle", &SET)]),
    ("cmp_set_le_32", &[spell("cmpl", &CMP_32), spell("setle", &SET)]),
    ("cmp_set_le_64", &[spell("cmpq", &CMP_64), spell("setle", &SET)]),
    ("cmp_set_g_8", &[spell("cmpb", &CMP_8), spell("setg", &SET)]),
    ("cmp_set_g_16", &[spell("cmpw", &CMP_16), spell("setg", &SET)]),
    ("cmp_set_g_32", &[spell("cmpl", &CMP_32), spell("setg", &SET)]),
    ("cmp_set_g_64", &[spell("cmpq", &CMP_64), spell("setg", &SET)]),
    ("cmp_set_ge_8", &[spell("cmpb", &CMP_8), spell("setge", &SET)]),
    ("cmp_set_ge_16", &[spell("cmpw", &CMP_16), spell("setge", &SET)]),
    ("cmp_set_ge_32", &[spell("cmpl", &CMP_32), spell("setge", &SET)]),
    ("cmp_set_ge_64", &[spell("cmpq", &CMP_64), spell("setge", &SET)]),
    ("cmp_set_b_8", &[spell("cmpb", &CMP_8), spell("setb", &SET)]),
    ("cmp_set_b_16", &[spell("cmpw", &CMP_16), spell("setb", &SET)]),
    ("cmp_set_b_32", &[spell("cmpl", &CMP_32), spell("setb", &SET)]),
    ("cmp_set_b_64", &[spell("cmpq", &CMP_64), spell("setb", &SET)]),
    ("cmp_set_be_8", &[spell("cmpb", &CMP_8), spell("setbe", &SET)]),
    ("cmp_set_be_16", &[spell("cmpw", &CMP_16), spell("setbe", &SET)]),
    ("cmp_set_be_32", &[spell("cmpl", &CMP_32), spell("setbe", &SET)]),
    ("cmp_set_be_64", &[spell("cmpq", &CMP_64), spell("setbe", &SET)]),
    ("cmp_set_a_8", &[spell("cmpb", &CMP_8), spell("seta", &SET)]),
    ("cmp_set_a_16", &[spell("cmpw", &CMP_16), spell("seta", &SET)]),
    ("cmp_set_a_32", &[spell("cmpl", &CMP_32), spell("seta", &SET)]),
    ("cmp_set_a_64", &[spell("cmpq", &CMP_64), spell("seta", &SET)]),
    ("cmp_set_ae_8", &[spell("cmpb", &CMP_8), spell("setae", &SET)]),
    ("cmp_set_ae_16", &[spell("cmpw", &CMP_16), spell("setae", &SET)]),
    ("cmp_set_ae_32", &[spell("cmpl", &CMP_32), spell("setae", &SET)]),
    ("cmp_set_ae_64", &[spell("cmpq", &CMP_64), spell("setae", &SET)]),
    // The conversions between widths. Widening to sixty four bits from thirty two is a thirty two
    // bit move, because every instruction that writes a thirty two bit register clears the half
    // above it, and taking the low bits of anything is a move of that many bits.
    ("movzx_8_16", &[spell("movzbw", &[Reg(1, Byte), Reg(0, Word)])]),
    ("movzx_8_32", &[spell("movzbl", &[Reg(1, Byte), Reg(0, Long)])]),
    ("movzx_8_64", &[spell("movzbq", &[Reg(1, Byte), Reg(0, Quad)])]),
    ("movzx_16_32", &[spell("movzwl", &[Reg(1, Word), Reg(0, Long)])]),
    ("movzx_16_64", &[spell("movzwq", &[Reg(1, Word), Reg(0, Quad)])]),
    ("mov_32_to_64", &[spell("movl", &[Reg(1, Long), Reg(0, Long)])]),
    ("movsx_8_16", &[spell("movsbw", &[Reg(1, Byte), Reg(0, Word)])]),
    ("movsx_8_32", &[spell("movsbl", &[Reg(1, Byte), Reg(0, Long)])]),
    ("movsx_8_64", &[spell("movsbq", &[Reg(1, Byte), Reg(0, Quad)])]),
    ("movsx_16_32", &[spell("movswl", &[Reg(1, Word), Reg(0, Long)])]),
    ("movsx_16_64", &[spell("movswq", &[Reg(1, Word), Reg(0, Quad)])]),
    ("movsxd_32_64", &[spell("movslq", &[Reg(1, Long), Reg(0, Quad)])]),
    // Widening a truth value. The byte it is in has its other seven bits zero, so widening the
    // byte is widening the bit and these are the byte widenings again, spelled the same and
    // named apart so the model can say what each of them means about the bit. Widening to a
    // byte is the move that puts it in the destination register and nothing more.
    ("bit_to_8", &[spell("movb", &[Reg(1, Byte), Reg(0, Byte)])]),
    ("bit_to_16", &[spell("movzbw", &[Reg(1, Byte), Reg(0, Word)])]),
    ("bit_to_32", &[spell("movzbl", &[Reg(1, Byte), Reg(0, Long)])]),
    ("bit_to_64", &[spell("movzbq", &[Reg(1, Byte), Reg(0, Quad)])]),
    ("low_8", &[spell("movb", &[Reg(1, Byte), Reg(0, Byte)])]),
    ("low_16", &[spell("movw", &[Reg(1, Word), Reg(0, Word)])]),
    ("low_32", &[spell("movl", &[Reg(1, Long), Reg(0, Long)])]),
    // The address computation the addressing modes are reached through.
    ("lea_64", &[spell("leaq", &[Mem, Reg(0, Quad)])]),
    // Reading and writing memory. The width is the width of what is moved rather than of the
    // address, which is sixty four bits in every one of them.
    ("mov_rm_8", &[spell("movb", &[Mem, Reg(0, Byte)])]),
    ("mov_rm_16", &[spell("movw", &[Mem, Reg(0, Word)])]),
    ("mov_rm_32", &[spell("movl", &[Mem, Reg(0, Long)])]),
    ("mov_rm_64", &[spell("movq", &[Mem, Reg(0, Quad)])]),
    ("mov_mr_8", &[spell("movb", &[Reg(0, Byte), Mem])]),
    ("mov_mr_16", &[spell("movw", &[Reg(0, Word), Mem])]),
    ("mov_mr_32", &[spell("movl", &[Reg(0, Long), Mem])]),
    ("mov_mr_64", &[spell("movq", &[Reg(0, Quad), Mem])]),
    // The three that are not instructions. A return value, an argument and the condition a block
    // leaves on are each one register and one claim about it, and the claim is for the allocator.
    ("ret_val_8", &[]),
    ("ret_val_16", &[]),
    ("ret_val_32", &[]),
    ("ret_val_64", &[]),
    ("ret_val_f32", &[]),
    ("ret_val_f64", &[]),
    ("arg_val_8", &[]),
    ("arg_val_16", &[]),
    ("arg_val_32", &[]),
    ("arg_val_64", &[]),
    ("arg_val_f32", &[]),
    ("arg_val_f64", &[]),
    ("br_cond_8", &[]),
    // A call goes to a name. What it passes and what comes back are in the operand vector and are
    // not written, because the machine does not read them and a reader of the assembly can see
    // them in the instructions above it.
    ("call", &[spell("call", &[Symbol])]),
    // A call through an address, which is the same mnemonic and a different instruction. The star
    // is the whole of the difference in the text and the addressing byte is the whole of it in the
    // bytes, and both come from the argument being a register rather than a place in the program.
    ("call_reg", &[spell("call", &[Through])]),
    // What a condition and the block layout come to.
    ("test_rr_8", &[spell("testb", &[Reg(0, Byte), Reg(0, Byte)])]),
    ("jcc_e", &[spell("je", &[Label])]),
    ("jcc_ne", &[spell("jne", &[Label])]),
    ("jmp", &[spell("jmp", &[Label])]),
    // What a copy, a prologue, an epilogue, a spill and a reload are made of. A vector register is
    // moved with the aligned form for the reason `crate::x86_64::FRAME` gives.
    ("mov_rr_64", &[spell("movq", &[Reg(1, Quad), Reg(0, Quad)])]),
    ("push_64", &[spell("pushq", &[Reg(0, Quad)])]),
    ("pop_64", &[spell("popq", &[Reg(0, Quad)])]),
    ("ret", &[spell("ret", &[])]),
    ("movaps_rr", &[spell("movaps", &[Xmm(1), Xmm(0)])]),
    ("movaps_rm", &[spell("movaps", &[Mem, Xmm(0)])]),
    ("movaps_mr", &[spell("movaps", &[Xmm(0), Mem])]),
    ("movss_rm", &[spell("movss", &[Mem, Xmm(0)])]),
    ("movsd_rm", &[spell("movsd", &[Mem, Xmm(0)])]),
    ("movss_mr", &[spell("movss", &[Xmm(0), Mem])]),
    ("movsd_mr", &[spell("movsd", &[Xmm(0), Mem])]),
    // The arithmetic is two address, so the destination is not written: it is the first source and
    // the allocator has already made the two the same register.
    ("addss_rr", &[spell("addss", &[Xmm(2), Xmm(0)])]),
    ("addsd_rr", &[spell("addsd", &[Xmm(2), Xmm(0)])]),
    ("subss_rr", &[spell("subss", &[Xmm(2), Xmm(0)])]),
    ("subsd_rr", &[spell("subsd", &[Xmm(2), Xmm(0)])]),
    ("mulss_rr", &[spell("mulss", &[Xmm(2), Xmm(0)])]),
    ("mulsd_rr", &[spell("mulsd", &[Xmm(2), Xmm(0)])]),
    ("divss_rr", &[spell("divss", &[Xmm(2), Xmm(0)])]),
    ("divsd_rr", &[spell("divsd", &[Xmm(2), Xmm(0)])]),
    // The conversions, which are the instructions with one argument from each file. The
    // destination is the operand at zero and the source is the one at one, the way every other
    // conversion here is written, and the assembler order puts the source first.
    //
    // The mnemonic carries the width of the integer, the way `movzbl` and `movzbq` do, because
    // that is what an assembler reads and what tells one row here from another: the two arguments
    // of `cvttsd2si` are a vector register and a general purpose one whichever width the answer
    // is, so nothing else about the instruction would say which of the two it is.
    ("cvtss2sd", &[spell("cvtss2sd", &[Xmm(1), Xmm(0)])]),
    ("cvtsd2ss", &[spell("cvtsd2ss", &[Xmm(1), Xmm(0)])]),
    ("cvttss2si_32", &[spell("cvttss2sil", &[Xmm(1), Reg(0, Long)])]),
    ("cvttss2si_64", &[spell("cvttss2siq", &[Xmm(1), Reg(0, Quad)])]),
    ("cvttsd2si_32", &[spell("cvttsd2sil", &[Xmm(1), Reg(0, Long)])]),
    ("cvttsd2si_64", &[spell("cvttsd2siq", &[Xmm(1), Reg(0, Quad)])]),
    ("cvtsi2ss_32", &[spell("cvtsi2ssl", &[Reg(1, Long), Xmm(0)])]),
    ("cvtsi2ss_64", &[spell("cvtsi2ssq", &[Reg(1, Quad), Xmm(0)])]),
    ("cvtsi2sd_32", &[spell("cvtsi2sdl", &[Reg(1, Long), Xmm(0)])]),
    ("cvtsi2sd_64", &[spell("cvtsi2sdq", &[Reg(1, Quad), Xmm(0)])]),
    ("movd_to_xmm", &[spell("movd", &[Reg(1, Long), Xmm(0)])]),
    ("movq_to_xmm", &[spell("movq", &[Reg(1, Quad), Xmm(0)])]),
    ("movd_from_xmm", &[spell("movd", &[Xmm(1), Reg(0, Long)])]),
    ("movq_from_xmm", &[spell("movq", &[Xmm(1), Reg(0, Quad)])]),
    // Comparing two floats, which is a `ucomiss` or a `ucomisd` and the byte a condition sets,
    // the same two instructions the integer comparisons above are. The compare is written with
    // the second source first, the way every AT&T instruction is, so the register the machine
    // treats as the left hand side is the one written last.
    //
    // `ucomisd` says four things in three flag bits, which is why these conditions are the ones
    // they are: above is greater and ordered, below is less or unordered, equal is equal or
    // unordered, and the parity flag on its own is the one that says the operands were not
    // ordered. A predicate that is one of those is one instruction pair.
    ("ucomiss_set_a", &[spell("ucomiss", &UCOMI), spell("seta", &SET)]),
    ("ucomiss_set_ae", &[spell("ucomiss", &UCOMI), spell("setae", &SET)]),
    ("ucomiss_set_b", &[spell("ucomiss", &UCOMI), spell("setb", &SET)]),
    ("ucomiss_set_be", &[spell("ucomiss", &UCOMI), spell("setbe", &SET)]),
    ("ucomiss_set_e", &[spell("ucomiss", &UCOMI), spell("sete", &SET)]),
    ("ucomiss_set_ne", &[spell("ucomiss", &UCOMI), spell("setne", &SET)]),
    ("ucomiss_set_p", &[spell("ucomiss", &UCOMI), spell("setp", &SET)]),
    ("ucomiss_set_np", &[spell("ucomiss", &UCOMI), spell("setnp", &SET)]),
    // The two that are not one condition. An ordered equality is the flag that means equal or
    // unordered together with the flag that says it was ordered, and its negation is the other
    // two put together the other way, so each of these is four instructions and the spare byte in
    // the middle of them is the operand at index one.
    (
        "ucomiss_set_e_and_np",
        &[
            spell("ucomiss", &UCOMI_BOTH),
            spell("sete", &SET),
            spell("setnp", &SET_SPARE),
            spell("andb", &COMBINE),
        ],
    ),
    (
        "ucomiss_set_ne_or_p",
        &[
            spell("ucomiss", &UCOMI_BOTH),
            spell("setne", &SET),
            spell("setp", &SET_SPARE),
            spell("orb", &COMBINE),
        ],
    ),
    ("ucomisd_set_a", &[spell("ucomisd", &UCOMI), spell("seta", &SET)]),
    ("ucomisd_set_ae", &[spell("ucomisd", &UCOMI), spell("setae", &SET)]),
    ("ucomisd_set_b", &[spell("ucomisd", &UCOMI), spell("setb", &SET)]),
    ("ucomisd_set_be", &[spell("ucomisd", &UCOMI), spell("setbe", &SET)]),
    ("ucomisd_set_e", &[spell("ucomisd", &UCOMI), spell("sete", &SET)]),
    ("ucomisd_set_ne", &[spell("ucomisd", &UCOMI), spell("setne", &SET)]),
    ("ucomisd_set_p", &[spell("ucomisd", &UCOMI), spell("setp", &SET)]),
    ("ucomisd_set_np", &[spell("ucomisd", &UCOMI), spell("setnp", &SET)]),
    (
        "ucomisd_set_e_and_np",
        &[
            spell("ucomisd", &UCOMI_BOTH),
            spell("sete", &SET),
            spell("setnp", &SET_SPARE),
            spell("andb", &COMBINE),
        ],
    ),
    (
        "ucomisd_set_ne_or_p",
        &[
            spell("ucomisd", &UCOMI_BOTH),
            spell("setne", &SET),
            spell("setp", &SET_SPARE),
            spell("orb", &COMBINE),
        ],
    ),
];

/// The instructions the opcode of that name is written as.
///
/// `None` for a name this target does not have, and an empty slice for one that is an opcode and
/// not an instruction, which are two different answers.
///
/// The name is written the way the machine IR holds it, so `add_rr_32` rather than `x64.add_rr_32`.
#[must_use]
pub fn written(name: &str) -> Option<&'static [Written]> {
    TEXT.iter().find(|(known, _)| *known == name).map(|&(_, insts)| insts)
}

/// The four spellings of each general purpose register, narrowest first.
///
/// In the order the registers are numbered, which is the encoding's order, so the row a register
/// is on is its number. The first four rows could each be spelled two ways for the byte: `al` is
/// the low byte of `rax` and `ah` is the one above it, and the second of those cannot be written
/// in an instruction that also names one of the eight registers x86-64 added. So the low byte is
/// what is here, and the one instruction that wants a high byte names it itself.
static GPR_TEXT: [[&str; 4]; 16] = [
    ["al", "ax", "eax", "rax"],
    ["cl", "cx", "ecx", "rcx"],
    ["dl", "dx", "edx", "rdx"],
    ["bl", "bx", "ebx", "rbx"],
    ["spl", "sp", "esp", "rsp"],
    ["bpl", "bp", "ebp", "rbp"],
    ["sil", "si", "esi", "rsi"],
    ["dil", "di", "edi", "rdi"],
    ["r8b", "r8w", "r8d", "r8"],
    ["r9b", "r9w", "r9d", "r9"],
    ["r10b", "r10w", "r10d", "r10"],
    ["r11b", "r11w", "r11d", "r11"],
    ["r12b", "r12w", "r12d", "r12"],
    ["r13b", "r13w", "r13d", "r13"],
    ["r14b", "r14w", "r14d", "r14"],
    ["r15b", "r15w", "r15d", "r15"],
];

/// What one general purpose register is called at that width, without the sigil.
///
/// `None` for a number that is not one of the sixteen. Every other class on this target has one
/// spelling per register, which is what the register file already holds, so this is the only place
/// a width changes a name.
#[must_use]
pub fn gpr_name(reg: PhysReg, width: Width) -> Option<&'static str> {
    GPR_TEXT.get(usize::from(reg.number())).map(|names| names[width.index()])
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::operand::Constraint;
    use crate::x86_64::insts::{Form, INSTS, form};
    use crate::x86_64::{GPR, REGS};

    /// Every index into the operand vector that one of these arguments names.
    fn named(insts: &[Written]) -> Vec<u8> {
        let mut at: Vec<u8> = insts
            .iter()
            .flat_map(|inst| inst.args)
            .filter_map(|arg| match *arg {
                Reg(at, _) | Xmm(at) => Some(at),
                _ => None,
            })
            .collect();
        at.sort_unstable();
        at.dedup();
        at
    }

    #[test]
    fn every_opcode_is_written_once_and_in_the_order_it_is_described_in() {
        let written: Vec<&str> = TEXT.iter().map(|&(name, _)| name).collect();
        let described: Vec<&str> = INSTS.iter().map(|&(name, _)| name).collect();
        // Same order and not merely the same set, because the two tables are read side by side
        // and a reader who has to search for the other half of an opcode will stop doing it.
        assert_eq!(written, described);
    }

    /// Everything an instruction is given has to come from somewhere the instruction has.
    ///
    /// Both directions. An argument that names an operand the form does not have would be read off
    /// the end of the vector, and an immediate or an address or a label the form carries and no
    /// instruction writes would be dropped on the floor, which is the failure that produces
    /// assembly that assembles and does the wrong thing.
    #[test]
    fn every_argument_names_something_the_instruction_really_has() {
        for &(name, insts) in TEXT {
            let form = form(name).expect("every written opcode is a described opcode");
            let operands = form.operands();
            let (mut imm, mut mem, mut symbol, mut label) = (false, false, false, false);
            let mut through = false;
            for arg in insts.iter().flat_map(|inst| inst.args) {
                match *arg {
                    Reg(at, _) | Xmm(at) => assert!(
                        usize::from(at) < operands.len(),
                        "{name} names operand {at} and has {} of them",
                        operands.len()
                    ),
                    // The other way round from the rest of them. A register the register file
                    // knows is one the allocator hands out, and an instruction that wants one of
                    // those should be carrying it as an operand so that it gets one.
                    Named(register) => assert!(
                        REGS.reg_named(register).is_none(),
                        "{name} names {register}, which is a register something could be in"
                    ),
                    Imm => imm = true,
                    Mem => mem = true,
                    Symbol => symbol = true,
                    Label => label = true,
                    // The one argument that names an operand without saying which, so there is no
                    // index to check against the form. What is checked is that only a call has
                    // one, and that a call has one of these or a symbol and never both: those are
                    // the two places a call can go and an instruction that named neither would go
                    // nowhere.
                    Through => through = true,
                }
            }
            assert_eq!(imm, form.takes_imm(), "{name} and its immediate disagree");
            assert_eq!(mem, form.takes_mem(), "{name} and its addressing mode disagree");
            assert_eq!(symbol || through, form == Form::Call, "{name} and where it goes disagree");
            assert!(!(symbol && through), "{name} goes to a name and through a register at once");
            assert_eq!(
                label,
                matches!(form, Form::Jcc | Form::Jmp),
                "{name} and where it goes disagree"
            );
        }
    }

    /// The other half of the same claim: an operand nothing names is one that is not written.
    ///
    /// Two kinds of operand are deliberately not named. The first source of a two-address
    /// instruction is the destination, which the allocator has arranged by now, so writing it
    /// again would be writing the same register twice. And a division names its dividend and both
    /// of its answers in the opcode, so all it is given is the divisor.
    #[test]
    fn an_operand_no_instruction_names_is_one_that_is_not_written() {
        for &(name, insts) in TEXT {
            let form = form(name).expect("every written opcode is a described opcode");
            if insts.is_empty() || matches!(form, Form::DivQuo | Form::DivRem) {
                continue;
            }
            let named = named(insts);
            let operands = form.operands();
            for at in 0..operands.len() {
                let tied = operands.iter().enumerate().any(|(other, operand)| {
                    operand.constraint == Constraint::Reuse(at as u8)
                        && named.contains(&(other as u8))
                });
                assert!(
                    named.contains(&(at as u8)) || tied,
                    "{name} has an operand {at} that nothing written for it names"
                );
            }
        }
    }

    #[test]
    fn an_opcode_that_is_not_an_instruction_is_written_as_no_instructions() {
        for name in ["ret_val_32", "arg_val_64", "ret_val_f64", "arg_val_f32", "br_cond_8"] {
            assert_eq!(written(name), Some([].as_slice()), "{name}");
        }
        for &(name, insts) in TEXT {
            let form = form(name).expect("every written opcode is a described opcode");
            assert_eq!(
                insts.is_empty(),
                matches!(
                    form,
                    Form::RetVal | Form::ArgVal | Form::RetValVec | Form::ArgValVec | Form::BrCond
                ),
                "{name} and whether it is an instruction disagree"
            );
        }
    }

    #[test]
    fn the_widest_spelling_of_a_register_is_the_one_the_register_file_gives_it() {
        // The register file holds one name per register and this holds four, and the widest of the
        // four is that one. A target that disagreed with itself here would print a register the
        // machine IR calls one thing under another name.
        for number in 0..16u8 {
            let reg = PhysReg::new(number);
            assert_eq!(gpr_name(reg, Quad), REGS.name(GPR, reg), "register {number}");
        }
        assert_eq!(gpr_name(PhysReg::new(16), Quad), None);
    }

    #[test]
    fn a_register_is_spelled_by_how_much_of_it_an_instruction_reads() {
        use crate::x86_64::{R8, RAX, RDI};

        assert_eq!(gpr_name(RAX, Byte), Some("al"));
        assert_eq!(gpr_name(RAX, Long), Some("eax"));
        // The three that are not the first letter of the wide name with an `e` in front of it,
        // which is where a table beats a rule.
        assert_eq!(gpr_name(RDI, Byte), Some("dil"));
        assert_eq!(gpr_name(RDI, Word), Some("di"));
        assert_eq!(gpr_name(R8, Long), Some("r8d"));
    }

    #[test]
    fn an_opcode_is_written_under_the_name_the_machine_ir_holds() {
        let add = written("add_rr_32").expect("an opcode this target has");
        assert_eq!(add, [spell("addl", &[Reg(2, Long), Reg(0, Long)])]);
        assert_eq!(written("x64.add_rr_32"), None, "the prefix is not part of the opcode");
        assert_eq!(written("add_rr_128"), None);
    }

    /// A spot check of the shapes that are more than one instruction, which are the ones a reader
    /// of `-S` is most likely to be surprised by and the ones an encoder has to agree with.
    #[test]
    fn an_opcode_the_machine_has_no_single_instruction_for_is_written_as_the_ones_it_has() {
        let compare = written("cmp_set_l_32").expect("an opcode this target has");
        assert_eq!(compare.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(), ["cmpl", "setl"]);

        let divide = written("idiv_quo_64").expect("an opcode this target has");
        assert_eq!(divide.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(), ["cqto", "idivq"]);

        // The one that is three, and the one place a register is named rather than allocated.
        let remainder = written("div_rem_8").expect("an opcode this target has");
        assert_eq!(
            remainder.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(),
            ["movzbl", "divb", "movb"]
        );
        assert_eq!(remainder[2].args, [Named("ah"), Reg(0, Byte)]);
    }
}