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 x = &Operand::new("x", Float);
149 let a = &Operand::new("a", Float);
150 let y = &Operand::new("y", Float);
151
152 ig.push(
153 Inst::new(
154 "x86_fmin",
155 r#"
156 Floating point minimum with x86 semantics.
157
158 This is equivalent to the C ternary operator `x < y ? x : y` which
159 differs from `fmin` when either operand is NaN or when comparing
160 +0.0 to -0.0.
161
162 When the two operands don't compare as LT, `y` is returned unchanged,
163 even if it is a signalling NaN.
164 "#,
165 &formats.binary,
166 )
167 .operands_in(vec![x, y])
168 .operands_out(vec![a]),
169 );
170
171 ig.push(
172 Inst::new(
173 "x86_fmax",
174 r#"
175 Floating point maximum with x86 semantics.
176
177 This is equivalent to the C ternary operator `x > y ? x : y` which
178 differs from `fmax` when either operand is NaN or when comparing
179 +0.0 to -0.0.
180
181 When the two operands don't compare as GT, `y` is returned unchanged,
182 even if it is a signalling NaN.
183 "#,
184 &formats.binary,
185 )
186 .operands_in(vec![x, y])
187 .operands_out(vec![a]),
188 );
189
190 let x = &Operand::new("x", iWord);
191
192 ig.push(
193 Inst::new(
194 "x86_push",
195 r#"
196 Pushes a value onto the stack.
197
198 Decrements the stack pointer and stores the specified value on to the top.
199
200 This is polymorphic in i32 and i64. However, it is only implemented for i64
201 in 64-bit mode, and only for i32 in 32-bit mode.
202 "#,
203 &formats.unary,
204 )
205 .operands_in(vec![x])
206 .other_side_effects(true)
207 .can_store(true),
208 );
209
210 ig.push(
211 Inst::new(
212 "x86_pop",
213 r#"
214 Pops a value from the stack.
215
216 Loads a value from the top of the stack and then increments the stack
217 pointer.
218
219 This is polymorphic in i32 and i64. However, it is only implemented for i64
220 in 64-bit mode, and only for i32 in 32-bit mode.
221 "#,
222 &formats.nullary,
223 )
224 .operands_out(vec![x])
225 .other_side_effects(true)
226 .can_load(true),
227 );
228
229 let y = &Operand::new("y", iWord);
230 let rflags = &Operand::new("rflags", iflags);
231
232 ig.push(
233 Inst::new(
234 "x86_bsr",
235 r#"
236 Bit Scan Reverse -- returns the bit-index of the most significant 1
237 in the word. Result is undefined if the argument is zero. However, it
238 sets the Z flag depending on the argument, so it is at least easy to
239 detect and handle that case.
240
241 This is polymorphic in i32 and i64. It is implemented for both i64 and
242 i32 in 64-bit mode, and only for i32 in 32-bit mode.
243 "#,
244 &formats.unary,
245 )
246 .operands_in(vec![x])
247 .operands_out(vec![y, rflags]),
248 );
249
250 ig.push(
251 Inst::new(
252 "x86_bsf",
253 r#"
254 Bit Scan Forwards -- returns the bit-index of the least significant 1
255 in the word. Is otherwise identical to 'bsr', just above.
256 "#,
257 &formats.unary,
258 )
259 .operands_in(vec![x])
260 .operands_out(vec![y, rflags]),
261 );
262
263 let uimm8 = &immediates.uimm8;
264 let TxN = &TypeVar::new(
265 "TxN",
266 "A SIMD vector type",
267 TypeSetBuilder::new()
268 .ints(Interval::All)
269 .floats(Interval::All)
270 .bools(Interval::All)
271 .simd_lanes(Interval::All)
272 .includes_scalars(false)
273 .build(),
274 );
275 let a = &Operand::new("a", TxN).with_doc("A vector value (i.e. held in an XMM register)");
276 let b = &Operand::new("b", TxN).with_doc("A vector value (i.e. held in an XMM register)");
277 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");
278
279 ig.push(
280 Inst::new(
281 "x86_pshufd",
282 r#"
283 Packed Shuffle Doublewords -- copies data from either memory or lanes in an extended
284 register and re-orders the data according to the passed immediate byte.
285 "#,
286 &formats.binary_imm8,
287 )
288 .operands_in(vec![a, i]) // TODO allow copying from memory here (need more permissive type than TxN)
289 .operands_out(vec![a]),
290 );
291
292 ig.push(
293 Inst::new(
294 "x86_pshufb",
295 r#"
296 Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle
297 mask from either memory or another extended register
298 "#,
299 &formats.binary,
300 )
301 .operands_in(vec![a, b]) // TODO allow re-ordering from memory here (need more permissive type than TxN)
302 .operands_out(vec![a]),
303 );
304
305 let Idx = &Operand::new("Idx", uimm8).with_doc("Lane index");
306 let x = &Operand::new("x", TxN);
307 let a = &Operand::new("a", &TxN.lane_of());
308
309 ig.push(
310 Inst::new(
311 "x86_pextr",
312 r#"
313 Extract lane ``Idx`` from ``x``.
314 The lane index, ``Idx``, is an immediate value, not an SSA value. It
315 must indicate a valid lane index for the type of ``x``.
316 "#,
317 &formats.binary_imm8,
318 )
319 .operands_in(vec![x, Idx])
320 .operands_out(vec![a]),
321 );
322
323 let IBxN = &TypeVar::new(
324 "IBxN",
325 "A SIMD vector type containing only booleans and integers",
326 TypeSetBuilder::new()
327 .ints(Interval::All)
328 .bools(Interval::All)
329 .simd_lanes(Interval::All)
330 .includes_scalars(false)
331 .build(),
332 );
333 let x = &Operand::new("x", IBxN);
334 let y = &Operand::new("y", &IBxN.lane_of()).with_doc("New lane value");
335 let a = &Operand::new("a", IBxN);
336
337 ig.push(
338 Inst::new(
339 "x86_pinsr",
340 r#"
341 Insert ``y`` into ``x`` at lane ``Idx``.
342 The lane index, ``Idx``, is an immediate value, not an SSA value. It
343 must indicate a valid lane index for the type of ``x``.
344 "#,
345 &formats.ternary_imm8,
346 )
347 .operands_in(vec![x, y, Idx])
348 .operands_out(vec![a]),
349 );
350
351 let FxN = &TypeVar::new(
352 "FxN",
353 "A SIMD vector type containing floats",
354 TypeSetBuilder::new()
355 .floats(Interval::All)
356 .simd_lanes(Interval::All)
357 .includes_scalars(false)
358 .build(),
359 );
360 let x = &Operand::new("x", FxN);
361 let y = &Operand::new("y", &FxN.lane_of()).with_doc("New lane value");
362 let a = &Operand::new("a", FxN);
363
364 ig.push(
365 Inst::new(
366 "x86_insertps",
367 r#"
368 Insert a lane of ``y`` into ``x`` at using ``Idx`` to encode both which lane the value is
369 extracted from and which it is inserted to. This is similar to x86_pinsr but inserts
370 floats, which are already stored in an XMM register.
371 "#,
372 &formats.ternary_imm8,
373 )
374 .operands_in(vec![x, y, Idx])
375 .operands_out(vec![a]),
376 );
377
378 let x = &Operand::new("x", TxN);
379 let y = &Operand::new("y", TxN);
380 let a = &Operand::new("a", TxN);
381
382 ig.push(
383 Inst::new(
384 "x86_punpckh",
385 r#"
386 Unpack the high-order lanes of ``x`` and ``y`` and interleave into ``a``. With notional
387 i8x4 vectors, where ``x = [x3, x2, x1, x0]`` and ``y = [y3, y2, y1, y0]``, this operation
388 would result in ``a = [y3, x3, y2, x2]`` (using the Intel manual's right-to-left lane
389 ordering).
390 "#,
391 &formats.binary,
392 )
393 .operands_in(vec![x, y])
394 .operands_out(vec![a]),
395 );
396
397 ig.push(
398 Inst::new(
399 "x86_punpckl",
400 r#"
401 Unpack the low-order lanes of ``x`` and ``y`` and interleave into ``a``. With notional
402 i8x4 vectors, where ``x = [x3, x2, x1, x0]`` and ``y = [y3, y2, y1, y0]``, this operation
403 would result in ``a = [y1, x1, y0, x0]`` (using the Intel manual's right-to-left lane
404 ordering).
405 "#,
406 &formats.binary,
407 )
408 .operands_in(vec![x, y])
409 .operands_out(vec![a]),
410 );
411
412 let I16xN = &TypeVar::new(
413 "I16xN",
414 "A SIMD vector type containing integers 16-bits wide and up",
415 TypeSetBuilder::new()
416 .ints(16..32)
417 .simd_lanes(4..8)
418 .includes_scalars(false)
419 .build(),
420 );
421
422 let x = &Operand::new("x", I16xN);
423 let y = &Operand::new("y", I16xN);
424 let a = &Operand::new("a", &I16xN.split_lanes());
425
426 ig.push(
427 Inst::new(
428 "x86_packss",
429 r#"
430 Convert packed signed integers the lanes of ``x`` and ``y`` into half-width integers, using
431 signed saturation to handle overflows. For example, with notional i16x2 vectors, where
432 ``x = [x1, x0]`` and ``y = [y1, y0]``, this operation would result in
433 ``a = [y1', y0', x1', x0']`` (using the Intel manual's right-to-left lane ordering).
434 "#,
435 &formats.binary,
436 )
437 .operands_in(vec![x, y])
438 .operands_out(vec![a]),
439 );
440
441 let x = &Operand::new("x", FxN);
442 let y = &Operand::new("y", FxN);
443 let a = &Operand::new("a", FxN);
444
445 ig.push(
446 Inst::new(
447 "x86_movsd",
448 r#"
449 Move the low 64 bits of the float vector ``y`` to the low 64 bits of float vector ``x``
450 "#,
451 &formats.binary,
452 )
453 .operands_in(vec![x, y])
454 .operands_out(vec![a]),
455 );
456
457 ig.push(
458 Inst::new(
459 "x86_movlhps",
460 r#"
461 Move the low 64 bits of the float vector ``y`` to the high 64 bits of float vector ``x``
462 "#,
463 &formats.binary,
464 )
465 .operands_in(vec![x, y])
466 .operands_out(vec![a]),
467 );
468
469 let IxN = &TypeVar::new(
470 "IxN",
471 "A SIMD vector type containing integers",
472 TypeSetBuilder::new()
473 .ints(Interval::All)
474 .simd_lanes(Interval::All)
475 .includes_scalars(false)
476 .build(),
477 );
478 let I128 = &TypeVar::new(
479 "I128",
480 "A SIMD vector type containing one large integer (due to Cranelift type constraints, \
481 this uses the Cranelift I64X2 type but should be understood as one large value, i.e., the \
482 upper lane is concatenated with the lower lane to form the integer)",
483 TypeSetBuilder::new()
484 .ints(64..64)
485 .simd_lanes(2..2)
486 .includes_scalars(false)
487 .build(),
488 );
489
490 let x = &Operand::new("x", IxN).with_doc("Vector value to shift");
491 let y = &Operand::new("y", I128).with_doc("Number of bits to shift");
492 let a = &Operand::new("a", IxN);
493
494 ig.push(
495 Inst::new(
496 "x86_psll",
497 r#"
498 Shift Packed Data Left Logical -- This implements the behavior of the shared instruction
499 ``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL*
500 family of instructions.
501 "#,
502 &formats.binary,
503 )
504 .operands_in(vec![x, y])
505 .operands_out(vec![a]),
506 );
507
508 ig.push(
509 Inst::new(
510 "x86_psrl",
511 r#"
512 Shift Packed Data Right Logical -- This implements the behavior of the shared instruction
513 ``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL*
514 family of instructions.
515 "#,
516 &formats.binary,
517 )
518 .operands_in(vec![x, y])
519 .operands_out(vec![a]),
520 );
521
522 ig.push(
523 Inst::new(
524 "x86_psra",
525 r#"
526 Shift Packed Data Right Arithmetic -- This implements the behavior of the shared
527 instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by
528 the PSRA* family of instructions.
529 "#,
530 &formats.binary,
531 )
532 .operands_in(vec![x, y])
533 .operands_out(vec![a]),
534 );
535
536 let I64x2 = &TypeVar::new(
537 "I64x2",
538 "A SIMD vector type containing two 64-bit integers",
539 TypeSetBuilder::new()
540 .ints(64..64)
541 .simd_lanes(2..2)
542 .includes_scalars(false)
543 .build(),
544 );
545
546 let x = &Operand::new("x", I64x2);
547 let y = &Operand::new("y", I64x2);
548 let a = &Operand::new("a", I64x2);
549 ig.push(
550 Inst::new(
551 "x86_pmullq",
552 r#"
553 Multiply Packed Integers -- Multiply two 64x2 integers and receive a 64x2 result with
554 lane-wise wrapping if the result overflows. This instruction is necessary to add distinct
555 encodings for CPUs with newer vector features.
556 "#,
557 &formats.binary,
558 )
559 .operands_in(vec![x, y])
560 .operands_out(vec![a]),
561 );
562
563 ig.push(
564 Inst::new(
565 "x86_pmuludq",
566 r#"
567 Multiply Packed Integers -- Using only the bottom 32 bits in each lane, multiply two 64x2
568 unsigned integers and receive a 64x2 result. This instruction avoids the need for handling
569 overflow as in `x86_pmullq`.
570 "#,
571 &formats.binary,
572 )
573 .operands_in(vec![x, y])
574 .operands_out(vec![a]),
575 );
576
577 let x = &Operand::new("x", TxN);
578 let y = &Operand::new("y", TxN);
579 let f = &Operand::new("f", iflags);
580 ig.push(
581 Inst::new(
582 "x86_ptest",
583 r#"
584 Logical Compare -- PTEST will set the ZF flag if all bits in the result are 0 of the
585 bitwise AND of the first source operand (first operand) and the second source operand
586 (second operand). PTEST sets the CF flag if all bits in the result are 0 of the bitwise
587 AND of the second source operand (second operand) and the logical NOT of the destination
588 operand (first operand).
589 "#,
590 &formats.binary,
591 )
592 .operands_in(vec![x, y])
593 .operands_out(vec![f]),
594 );
595
596 let x = &Operand::new("x", IxN);
597 let y = &Operand::new("y", IxN);
598 let a = &Operand::new("a", IxN);
599 ig.push(
600 Inst::new(
601 "x86_pmaxs",
602 r#"
603 Maximum of Packed Signed Integers -- Compare signed integers in the first and second
604 operand and return the maximum values.
605 "#,
606 &formats.binary,
607 )
608 .operands_in(vec![x, y])
609 .operands_out(vec![a]),
610 );
611
612 ig.push(
613 Inst::new(
614 "x86_pmaxu",
615 r#"
616 Maximum of Packed Unsigned Integers -- Compare unsigned integers in the first and second
617 operand and return the maximum values.
618 "#,
619 &formats.binary,
620 )
621 .operands_in(vec![x, y])
622 .operands_out(vec![a]),
623 );
624
625 ig.push(
626 Inst::new(
627 "x86_pmins",
628 r#"
629 Minimum of Packed Signed Integers -- Compare signed integers in the first and second
630 operand and return the minimum values.
631 "#,
632 &formats.binary,
633 )
634 .operands_in(vec![x, y])
635 .operands_out(vec![a]),
636 );
637
638 ig.push(
639 Inst::new(
640 "x86_pminu",
641 r#"
642 Minimum of Packed Unsigned Integers -- Compare unsigned integers in the first and second
643 operand and return the minimum values.
644 "#,
645 &formats.binary,
646 )
647 .operands_in(vec![x, y])
648 .operands_out(vec![a]),
649 );
650
651 let i64_t = &TypeVar::new(
652 "i64_t",
653 "A scalar 64bit integer",
654 TypeSetBuilder::new().ints(64..64).build(),
655 );
656
657 let GV = &Operand::new("GV", &entities.global_value);
658 let addr = &Operand::new("addr", i64_t);
659
660 ig.push(
661 Inst::new(
662 "x86_elf_tls_get_addr",
663 r#"
664 Elf tls get addr -- This implements the GD TLS model for ELF. The clobber output should
665 not be used.
666 "#,
667 &formats.unary_global_value,
668 )
669 // This is a bit overly broad to mark as clobbering *all* the registers, because it should
670 // only preserve caller-saved registers. There's no way to indicate this to register
671 // allocation yet, though, so mark as clobbering all registers instead.
672 .clobbers_all_regs(true)
673 .operands_in(vec![GV])
674 .operands_out(vec![addr]),
675 );
676 ig.push(
677 Inst::new(
678 "x86_macho_tls_get_addr",
679 r#"
680 Mach-O tls get addr -- This implements TLS access for Mach-O. The clobber output should
681 not be used.
682 "#,
683 &formats.unary_global_value,
684 )
685 // See above comment for x86_elf_tls_get_addr.
686 .clobbers_all_regs(true)
687 .operands_in(vec![GV])
688 .operands_out(vec![addr]),
689 );
690
691 ig.build()
692 }
693