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