1 #![allow(non_snake_case)]
2 
3 use crate::cdsl::instructions::{
4     AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder,
5 };
6 use crate::cdsl::operands::Operand;
7 use crate::cdsl::types::ValueType;
8 use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
9 use crate::shared::entities::EntityRefs;
10 use crate::shared::formats::Formats;
11 use crate::shared::immediates::Immediates;
12 use crate::shared::types;
13 
14 #[allow(clippy::many_single_char_names)]
define( mut all_instructions: &mut AllInstructions, formats: &Formats, immediates: &Immediates, entities: &EntityRefs, ) -> InstructionGroup15 pub(crate) fn define(
16     mut all_instructions: &mut AllInstructions,
17     formats: &Formats,
18     immediates: &Immediates,
19     entities: &EntityRefs,
20 ) -> InstructionGroup {
21     let mut ig = InstructionGroupBuilder::new(&mut all_instructions);
22 
23     let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
24 
25     let iWord = &TypeVar::new(
26         "iWord",
27         "A scalar integer machine word",
28         TypeSetBuilder::new().ints(32..64).build(),
29     );
30     let nlo = &Operand::new("nlo", iWord).with_doc("Low part of numerator");
31     let nhi = &Operand::new("nhi", iWord).with_doc("High part of numerator");
32     let d = &Operand::new("d", iWord).with_doc("Denominator");
33     let q = &Operand::new("q", iWord).with_doc("Quotient");
34     let r = &Operand::new("r", iWord).with_doc("Remainder");
35 
36     ig.push(
37         Inst::new(
38             "x86_udivmodx",
39             r#"
40         Extended unsigned division.
41 
42         Concatenate the bits in `nhi` and `nlo` to form the numerator.
43         Interpret the bits as an unsigned number and divide by the unsigned
44         denominator `d`. Trap when `d` is zero or if the quotient is larger
45         than the range of the output.
46 
47         Return both quotient and remainder.
48         "#,
49             &formats.ternary,
50         )
51         .operands_in(vec![nlo, nhi, d])
52         .operands_out(vec![q, r])
53         .can_trap(true),
54     );
55 
56     ig.push(
57         Inst::new(
58             "x86_sdivmodx",
59             r#"
60         Extended signed division.
61 
62         Concatenate the bits in `nhi` and `nlo` to form the numerator.
63         Interpret the bits as a signed number and divide by the signed
64         denominator `d`. Trap when `d` is zero or if the quotient is outside
65         the range of the output.
66 
67         Return both quotient and remainder.
68         "#,
69             &formats.ternary,
70         )
71         .operands_in(vec![nlo, nhi, d])
72         .operands_out(vec![q, r])
73         .can_trap(true),
74     );
75 
76     let argL = &Operand::new("argL", iWord);
77     let argR = &Operand::new("argR", iWord);
78     let resLo = &Operand::new("resLo", iWord);
79     let resHi = &Operand::new("resHi", iWord);
80 
81     ig.push(
82         Inst::new(
83             "x86_umulx",
84             r#"
85         Unsigned integer multiplication, producing a double-length result.
86 
87         Polymorphic over all scalar integer types, but does not support vector
88         types.
89         "#,
90             &formats.binary,
91         )
92         .operands_in(vec![argL, argR])
93         .operands_out(vec![resLo, resHi]),
94     );
95 
96     ig.push(
97         Inst::new(
98             "x86_smulx",
99             r#"
100         Signed integer multiplication, producing a double-length result.
101 
102         Polymorphic over all scalar integer types, but does not support vector
103         types.
104         "#,
105             &formats.binary,
106         )
107         .operands_in(vec![argL, argR])
108         .operands_out(vec![resLo, resHi]),
109     );
110 
111     let Float = &TypeVar::new(
112         "Float",
113         "A scalar or vector floating point number",
114         TypeSetBuilder::new()
115             .floats(Interval::All)
116             .simd_lanes(Interval::All)
117             .build(),
118     );
119     let IntTo = &TypeVar::new(
120         "IntTo",
121         "An integer type with the same number of lanes",
122         TypeSetBuilder::new()
123             .ints(32..64)
124             .simd_lanes(Interval::All)
125             .build(),
126     );
127     let x = &Operand::new("x", Float);
128     let a = &Operand::new("a", IntTo);
129 
130     ig.push(
131         Inst::new(
132             "x86_cvtt2si",
133             r#"
134         Convert with truncation floating point to signed integer.
135 
136         The source floating point operand is converted to a signed integer by
137         rounding towards zero. If the result can't be represented in the output
138         type, returns the smallest signed value the output type can represent.
139 
140         This instruction does not trap.
141         "#,
142             &formats.unary,
143         )
144         .operands_in(vec![x])
145         .operands_out(vec![a]),
146     );
147 
148     let f32x4 = &TypeVar::new(
149         "f32x4",
150         "A floating point number",
151         TypeSetBuilder::new()
152             .floats(32..32)
153             .simd_lanes(4..4)
154             .build(),
155     );
156     let i32x4 = &TypeVar::new(
157         "i32x4",
158         "An integer type with the same number of lanes",
159         TypeSetBuilder::new().ints(32..32).simd_lanes(4..4).build(),
160     );
161     let x = &Operand::new("x", i32x4);
162     let a = &Operand::new("a", f32x4);
163 
164     ig.push(
165         Inst::new(
166             "x86_vcvtudq2ps",
167             r#"
168         Convert unsigned integer to floating point.
169 
170         Convert packed doubleword unsigned integers to packed single-precision floating-point
171         values. This instruction does not trap.
172         "#,
173             &formats.unary,
174         )
175         .operands_in(vec![x])
176         .operands_out(vec![a]),
177     );
178 
179     let x = &Operand::new("x", Float);
180     let a = &Operand::new("a", Float);
181     let y = &Operand::new("y", Float);
182 
183     ig.push(
184         Inst::new(
185             "x86_fmin",
186             r#"
187         Floating point minimum with x86 semantics.
188 
189         This is equivalent to the C ternary operator `x < y ? x : y` which
190         differs from `fmin` when either operand is NaN or when comparing
191         +0.0 to -0.0.
192 
193         When the two operands don't compare as LT, `y` is returned unchanged,
194         even if it is a signalling NaN.
195         "#,
196             &formats.binary,
197         )
198         .operands_in(vec![x, y])
199         .operands_out(vec![a]),
200     );
201 
202     ig.push(
203         Inst::new(
204             "x86_fmax",
205             r#"
206         Floating point maximum with x86 semantics.
207 
208         This is equivalent to the C ternary operator `x > y ? x : y` which
209         differs from `fmax` when either operand is NaN or when comparing
210         +0.0 to -0.0.
211 
212         When the two operands don't compare as GT, `y` is returned unchanged,
213         even if it is a signalling NaN.
214         "#,
215             &formats.binary,
216         )
217         .operands_in(vec![x, y])
218         .operands_out(vec![a]),
219     );
220 
221     let x = &Operand::new("x", iWord);
222 
223     ig.push(
224         Inst::new(
225             "x86_push",
226             r#"
227     Pushes a value onto the stack.
228 
229     Decrements the stack pointer and stores the specified value on to the top.
230 
231     This is polymorphic in i32 and i64. However, it is only implemented for i64
232     in 64-bit mode, and only for i32 in 32-bit mode.
233     "#,
234             &formats.unary,
235         )
236         .operands_in(vec![x])
237         .other_side_effects(true)
238         .can_store(true),
239     );
240 
241     ig.push(
242         Inst::new(
243             "x86_pop",
244             r#"
245     Pops a value from the stack.
246 
247     Loads a value from the top of the stack and then increments the stack
248     pointer.
249 
250     This is polymorphic in i32 and i64. However, it is only implemented for i64
251     in 64-bit mode, and only for i32 in 32-bit mode.
252     "#,
253             &formats.nullary,
254         )
255         .operands_out(vec![x])
256         .other_side_effects(true)
257         .can_load(true),
258     );
259 
260     let y = &Operand::new("y", iWord);
261     let rflags = &Operand::new("rflags", iflags);
262 
263     ig.push(
264         Inst::new(
265             "x86_bsr",
266             r#"
267     Bit Scan Reverse -- returns the bit-index of the most significant 1
268     in the word. Result is undefined if the argument is zero. However, it
269     sets the Z flag depending on the argument, so it is at least easy to
270     detect and handle that case.
271 
272     This is polymorphic in i32 and i64. It is implemented for both i64 and
273     i32 in 64-bit mode, and only for i32 in 32-bit mode.
274     "#,
275             &formats.unary,
276         )
277         .operands_in(vec![x])
278         .operands_out(vec![y, rflags]),
279     );
280 
281     ig.push(
282         Inst::new(
283             "x86_bsf",
284             r#"
285     Bit Scan Forwards -- returns the bit-index of the least significant 1
286     in the word. Is otherwise identical to 'bsr', just above.
287     "#,
288             &formats.unary,
289         )
290         .operands_in(vec![x])
291         .operands_out(vec![y, rflags]),
292     );
293 
294     let uimm8 = &immediates.uimm8;
295     let TxN = &TypeVar::new(
296         "TxN",
297         "A SIMD vector type",
298         TypeSetBuilder::new()
299             .ints(Interval::All)
300             .floats(Interval::All)
301             .bools(Interval::All)
302             .simd_lanes(Interval::All)
303             .includes_scalars(false)
304             .build(),
305     );
306     let a = &Operand::new("a", TxN).with_doc("A vector value (i.e. held in an XMM register)");
307     let b = &Operand::new("b", TxN).with_doc("A vector value (i.e. held in an XMM register)");
308     let i = &Operand::new("i", uimm8).with_doc("An ordering operand controlling the copying of data from the source to the destination; see PSHUFD in Intel manual for details");
309 
310     ig.push(
311         Inst::new(
312             "x86_pshufd",
313             r#"
314     Packed Shuffle Doublewords -- copies data from either memory or lanes in an extended
315     register and re-orders the data according to the passed immediate byte.
316     "#,
317             &formats.binary_imm8,
318         )
319         .operands_in(vec![a, i]) // TODO allow copying from memory here (need more permissive type than TxN)
320         .operands_out(vec![a]),
321     );
322 
323     ig.push(
324         Inst::new(
325             "x86_pshufb",
326             r#"
327     Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle
328     mask from either memory or another extended register
329     "#,
330             &formats.binary,
331         )
332         .operands_in(vec![a, b]) // TODO allow re-ordering from memory here (need more permissive type than TxN)
333         .operands_out(vec![a]),
334     );
335 
336     let mask = &Operand::new("mask", uimm8).with_doc("mask to select lanes from b");
337     ig.push(
338         Inst::new(
339             "x86_pblendw",
340             r#"
341     Blend packed words using an immediate mask. Each bit of the 8-bit immediate corresponds to a
342     lane in ``b``: if the bit is set, the lane is copied into ``a``.
343     "#,
344             &formats.ternary_imm8,
345         )
346         .operands_in(vec![a, b, mask])
347         .operands_out(vec![a]),
348     );
349 
350     let Idx = &Operand::new("Idx", uimm8).with_doc("Lane index");
351     let x = &Operand::new("x", TxN);
352     let a = &Operand::new("a", &TxN.lane_of());
353 
354     ig.push(
355         Inst::new(
356             "x86_pextr",
357             r#"
358         Extract lane ``Idx`` from ``x``.
359         The lane index, ``Idx``, is an immediate value, not an SSA value. It
360         must indicate a valid lane index for the type of ``x``.
361         "#,
362             &formats.binary_imm8,
363         )
364         .operands_in(vec![x, Idx])
365         .operands_out(vec![a]),
366     );
367 
368     let IBxN = &TypeVar::new(
369         "IBxN",
370         "A SIMD vector type containing only booleans and integers",
371         TypeSetBuilder::new()
372             .ints(Interval::All)
373             .bools(Interval::All)
374             .simd_lanes(Interval::All)
375             .includes_scalars(false)
376             .build(),
377     );
378     let x = &Operand::new("x", IBxN);
379     let y = &Operand::new("y", &IBxN.lane_of()).with_doc("New lane value");
380     let a = &Operand::new("a", IBxN);
381 
382     ig.push(
383         Inst::new(
384             "x86_pinsr",
385             r#"
386         Insert ``y`` into ``x`` at lane ``Idx``.
387         The lane index, ``Idx``, is an immediate value, not an SSA value. It
388         must indicate a valid lane index for the type of ``x``.
389         "#,
390             &formats.ternary_imm8,
391         )
392         .operands_in(vec![x, y, Idx])
393         .operands_out(vec![a]),
394     );
395 
396     let FxN = &TypeVar::new(
397         "FxN",
398         "A SIMD vector type containing floats",
399         TypeSetBuilder::new()
400             .floats(Interval::All)
401             .simd_lanes(Interval::All)
402             .includes_scalars(false)
403             .build(),
404     );
405     let x = &Operand::new("x", FxN);
406     let y = &Operand::new("y", &FxN.lane_of()).with_doc("New lane value");
407     let a = &Operand::new("a", FxN);
408 
409     ig.push(
410         Inst::new(
411             "x86_insertps",
412             r#"
413         Insert a lane of ``y`` into ``x`` at using ``Idx`` to encode both which lane the value is
414         extracted from and which it is inserted to. This is similar to x86_pinsr but inserts
415         floats, which are already stored in an XMM register.
416         "#,
417             &formats.ternary_imm8,
418         )
419         .operands_in(vec![x, y, Idx])
420         .operands_out(vec![a]),
421     );
422 
423     let x = &Operand::new("x", TxN);
424     let y = &Operand::new("y", TxN);
425     let a = &Operand::new("a", TxN);
426 
427     ig.push(
428         Inst::new(
429             "x86_punpckh",
430             r#"
431         Unpack the high-order lanes of ``x`` and ``y`` and interleave into ``a``. With notional
432         i8x4 vectors, where ``x = [x3, x2, x1, x0]`` and ``y = [y3, y2, y1, y0]``, this operation
433         would result in ``a = [y3, x3, y2, x2]`` (using the Intel manual's right-to-left lane
434         ordering).
435         "#,
436             &formats.binary,
437         )
438         .operands_in(vec![x, y])
439         .operands_out(vec![a]),
440     );
441 
442     ig.push(
443         Inst::new(
444             "x86_punpckl",
445             r#"
446         Unpack the low-order lanes of ``x`` and ``y`` and interleave into ``a``. With notional
447         i8x4 vectors, where ``x = [x3, x2, x1, x0]`` and ``y = [y3, y2, y1, y0]``, this operation
448         would result in ``a = [y1, x1, y0, x0]`` (using the Intel manual's right-to-left lane
449         ordering).
450         "#,
451             &formats.binary,
452         )
453         .operands_in(vec![x, y])
454         .operands_out(vec![a]),
455     );
456 
457     let x = &Operand::new("x", FxN);
458     let y = &Operand::new("y", FxN);
459     let a = &Operand::new("a", FxN);
460 
461     ig.push(
462         Inst::new(
463             "x86_movsd",
464             r#"
465         Move the low 64 bits of the float vector ``y`` to the low 64 bits of float vector ``x``
466         "#,
467             &formats.binary,
468         )
469         .operands_in(vec![x, y])
470         .operands_out(vec![a]),
471     );
472 
473     ig.push(
474         Inst::new(
475             "x86_movlhps",
476             r#"
477         Move the low 64 bits of the float vector ``y`` to the high 64 bits of float vector ``x``
478         "#,
479             &formats.binary,
480         )
481         .operands_in(vec![x, y])
482         .operands_out(vec![a]),
483     );
484 
485     let IxN = &TypeVar::new(
486         "IxN",
487         "A SIMD vector type containing integers",
488         TypeSetBuilder::new()
489             .ints(Interval::All)
490             .simd_lanes(Interval::All)
491             .includes_scalars(false)
492             .build(),
493     );
494     let I128 = &TypeVar::new(
495         "I128",
496         "A SIMD vector type containing one large integer (due to Cranelift type constraints, \
497         this uses the Cranelift I64X2 type but should be understood as one large value, i.e., the \
498         upper lane is concatenated with the lower lane to form the integer)",
499         TypeSetBuilder::new()
500             .ints(64..64)
501             .simd_lanes(2..2)
502             .includes_scalars(false)
503             .build(),
504     );
505 
506     let x = &Operand::new("x", IxN).with_doc("Vector value to shift");
507     let y = &Operand::new("y", I128).with_doc("Number of bits to shift");
508     let a = &Operand::new("a", IxN);
509 
510     ig.push(
511         Inst::new(
512             "x86_psll",
513             r#"
514         Shift Packed Data Left Logical -- This implements the behavior of the shared instruction
515         ``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL*
516         family of instructions.
517         "#,
518             &formats.binary,
519         )
520         .operands_in(vec![x, y])
521         .operands_out(vec![a]),
522     );
523 
524     ig.push(
525         Inst::new(
526             "x86_psrl",
527             r#"
528         Shift Packed Data Right Logical -- This implements the behavior of the shared instruction
529         ``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL*
530         family of instructions.
531         "#,
532             &formats.binary,
533         )
534         .operands_in(vec![x, y])
535         .operands_out(vec![a]),
536     );
537 
538     ig.push(
539         Inst::new(
540             "x86_psra",
541             r#"
542         Shift Packed Data Right Arithmetic -- This implements the behavior of the shared
543         instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by
544         the PSRA* family of instructions.
545         "#,
546             &formats.binary,
547         )
548         .operands_in(vec![x, y])
549         .operands_out(vec![a]),
550     );
551 
552     let I64x2 = &TypeVar::new(
553         "I64x2",
554         "A SIMD vector type containing two 64-bit integers",
555         TypeSetBuilder::new()
556             .ints(64..64)
557             .simd_lanes(2..2)
558             .includes_scalars(false)
559             .build(),
560     );
561 
562     let x = &Operand::new("x", I64x2);
563     let y = &Operand::new("y", I64x2);
564     let a = &Operand::new("a", I64x2);
565     ig.push(
566         Inst::new(
567             "x86_pmullq",
568             r#"
569         Multiply Packed Integers -- Multiply two 64x2 integers and receive a 64x2 result with
570         lane-wise wrapping if the result overflows. This instruction is necessary to add distinct
571         encodings for CPUs with newer vector features.
572         "#,
573             &formats.binary,
574         )
575         .operands_in(vec![x, y])
576         .operands_out(vec![a]),
577     );
578 
579     ig.push(
580         Inst::new(
581             "x86_pmuludq",
582             r#"
583         Multiply Packed Integers -- Using only the bottom 32 bits in each lane, multiply two 64x2
584         unsigned integers and receive a 64x2 result. This instruction avoids the need for handling
585         overflow as in `x86_pmullq`.
586         "#,
587             &formats.binary,
588         )
589         .operands_in(vec![x, y])
590         .operands_out(vec![a]),
591     );
592 
593     let x = &Operand::new("x", TxN);
594     let y = &Operand::new("y", TxN);
595     let f = &Operand::new("f", iflags);
596     ig.push(
597         Inst::new(
598             "x86_ptest",
599             r#"
600         Logical Compare -- PTEST will set the ZF flag if all bits in the result are 0 of the
601         bitwise AND of the first source operand (first operand) and the second source operand
602         (second operand). PTEST sets the CF flag if all bits in the result are 0 of the bitwise
603         AND of the second source operand (second operand) and the logical NOT of the destination
604         operand (first operand).
605         "#,
606             &formats.binary,
607         )
608         .operands_in(vec![x, y])
609         .operands_out(vec![f]),
610     );
611 
612     let x = &Operand::new("x", IxN);
613     let y = &Operand::new("y", IxN);
614     let a = &Operand::new("a", IxN);
615     ig.push(
616         Inst::new(
617             "x86_pmaxs",
618             r#"
619         Maximum of Packed Signed Integers -- Compare signed integers in the first and second
620         operand and return the maximum values.
621         "#,
622             &formats.binary,
623         )
624         .operands_in(vec![x, y])
625         .operands_out(vec![a]),
626     );
627 
628     ig.push(
629         Inst::new(
630             "x86_pmaxu",
631             r#"
632         Maximum of Packed Unsigned Integers -- Compare unsigned integers in the first and second
633         operand and return the maximum values.
634         "#,
635             &formats.binary,
636         )
637         .operands_in(vec![x, y])
638         .operands_out(vec![a]),
639     );
640 
641     ig.push(
642         Inst::new(
643             "x86_pmins",
644             r#"
645         Minimum of Packed Signed Integers -- Compare signed integers in the first and second
646         operand and return the minimum values.
647         "#,
648             &formats.binary,
649         )
650         .operands_in(vec![x, y])
651         .operands_out(vec![a]),
652     );
653 
654     ig.push(
655         Inst::new(
656             "x86_pminu",
657             r#"
658         Minimum of Packed Unsigned Integers -- Compare unsigned integers in the first and second
659         operand and return the minimum values.
660         "#,
661             &formats.binary,
662         )
663         .operands_in(vec![x, y])
664         .operands_out(vec![a]),
665     );
666 
667     let c = &Operand::new("c", uimm8)
668         .with_doc("The number of bytes to shift right; see PALIGNR in Intel manual for details");
669     ig.push(
670         Inst::new(
671             "x86_palignr",
672             r#"
673         Concatenate destination and source operands, extracting a byte-aligned result shifted to
674         the right by `c`.
675         "#,
676             &formats.ternary_imm8,
677         )
678         .operands_in(vec![x, y, c])
679         .operands_out(vec![a]),
680     );
681 
682     let i64_t = &TypeVar::new(
683         "i64_t",
684         "A scalar 64bit integer",
685         TypeSetBuilder::new().ints(64..64).build(),
686     );
687 
688     let GV = &Operand::new("GV", &entities.global_value);
689     let addr = &Operand::new("addr", i64_t);
690 
691     ig.push(
692         Inst::new(
693             "x86_elf_tls_get_addr",
694             r#"
695         Elf tls get addr -- This implements the GD TLS model for ELF. The clobber output should
696         not be used.
697             "#,
698             &formats.unary_global_value,
699         )
700         // This is a bit overly broad to mark as clobbering *all* the registers, because it should
701         // only preserve caller-saved registers. There's no way to indicate this to register
702         // allocation yet, though, so mark as clobbering all registers instead.
703         .clobbers_all_regs(true)
704         .operands_in(vec![GV])
705         .operands_out(vec![addr]),
706     );
707     ig.push(
708         Inst::new(
709             "x86_macho_tls_get_addr",
710             r#"
711         Mach-O tls get addr -- This implements TLS access for Mach-O. The clobber output should
712         not be used.
713             "#,
714             &formats.unary_global_value,
715         )
716         // See above comment for x86_elf_tls_get_addr.
717         .clobbers_all_regs(true)
718         .operands_in(vec![GV])
719         .operands_out(vec![addr]),
720     );
721 
722     ig.build()
723 }
724