WIP: adding support for carry input/outputs and simplifying python API
[power-instruction-analyzer.git] / src / lib.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
3
4 #![cfg_attr(feature = "native_instrs", feature(llvm_asm))]
5
6 #[cfg(all(feature = "native_instrs", not(target_arch = "powerpc64")))]
7 compile_error!("native_instrs feature requires target_arch to be powerpc64");
8
9 pub mod instr_models;
10 mod serde_hex;
11
12 use serde::{Deserialize, Serialize};
13 use std::{
14 cmp::Ordering,
15 fmt,
16 ops::{Index, IndexMut},
17 };
18 use serde_plain::forward_display_to_serde;
19
20 fn is_default<T: Default + PartialEq>(v: &T) -> bool {
21 T::default() == *v
22 }
23
24 // powerpc bit numbers count from MSB to LSB
25 const fn get_xer_bit_mask(powerpc_bit_num: usize) -> u64 {
26 (1 << 63) >> powerpc_bit_num
27 }
28
29 macro_rules! xer_subset {
30 (
31 $struct_vis:vis struct $struct_name:ident {
32 $(
33 #[bit($powerpc_bit_num:expr, $mask_name:ident)]
34 $field_vis:vis $field_name:ident: bool,
35 )+
36 }
37 ) => {
38 #[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
39 $struct_vis struct $struct_name {
40 $(
41 $field_vis $field_name: bool,
42 )+
43 }
44
45 impl $struct_name {
46 $(
47 $field_vis const $mask_name: u64 = get_xer_bit_mask($powerpc_bit_num);
48 )+
49 pub const fn from_xer(xer: u64) -> Self {
50 Self {
51 $(
52 $field_name: (xer & Self::$mask_name) != 0,
53 )+
54 }
55 }
56 pub const fn to_xer(self) -> u64 {
57 let mut retval = 0u64;
58 $(
59 if self.$field_name {
60 retval |= Self::$mask_name;
61 }
62 )+
63 retval
64 }
65 }
66 };
67 }
68
69 xer_subset! {
70 pub struct OverflowFlags {
71 #[bit(32, XER_SO_MASK)]
72 pub so: bool,
73 #[bit(33, XER_OV_MASK)]
74 pub ov: bool,
75 #[bit(44, XER_OV32_MASK)]
76 pub ov32: bool,
77 }
78 }
79
80 impl OverflowFlags {
81 pub const fn from_overflow(overflow: bool) -> Self {
82 Self {
83 so: overflow,
84 ov: overflow,
85 ov32: overflow,
86 }
87 }
88 }
89
90 xer_subset! {
91 pub struct CarryFlags {
92 #[bit(34, XER_CA_MASK)]
93 pub ca: bool,
94 #[bit(45, XER_CA32_MASK)]
95 pub ca32: bool,
96 }
97 }
98
99 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
100 pub struct ConditionRegister {
101 pub lt: bool,
102 pub gt: bool,
103 pub eq: bool,
104 pub so: bool,
105 }
106
107 impl ConditionRegister {
108 pub const fn from_4_bits(bits: u8) -> Self {
109 // assert bits is 4-bits long
110 // can switch to using assert! once rustc feature const_panic is stabilized
111 [0; 0x10][bits as usize];
112
113 Self {
114 lt: (bits & 8) != 0,
115 gt: (bits & 4) != 0,
116 eq: (bits & 2) != 0,
117 so: (bits & 1) != 0,
118 }
119 }
120 pub const CR_FIELD_COUNT: usize = 8;
121 pub const fn from_cr_field(cr: u32, field_index: usize) -> Self {
122 // assert field_index is less than CR_FIELD_COUNT
123 // can switch to using assert! once rustc feature const_panic is stabilized
124 [0; Self::CR_FIELD_COUNT][field_index];
125
126 let reversed_field_index = Self::CR_FIELD_COUNT - field_index - 1;
127 let bits = (cr >> (4 * reversed_field_index)) & 0xF;
128 Self::from_4_bits(bits as u8)
129 }
130 pub fn from_signed_int<T: Ord + Default>(value: T, so: bool) -> Self {
131 let ordering = value.cmp(&T::default());
132 Self {
133 lt: ordering == Ordering::Less,
134 gt: ordering == Ordering::Greater,
135 eq: ordering == Ordering::Equal,
136 so,
137 }
138 }
139 }
140
141 #[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
142 pub struct InstructionResult {
143 #[serde(
144 default,
145 skip_serializing_if = "Option::is_none",
146 with = "serde_hex::SerdeHex"
147 )]
148 pub rt: Option<u64>,
149 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
150 pub overflow: Option<OverflowFlags>,
151 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
152 pub carry: Option<CarryFlags>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 pub cr0: Option<ConditionRegister>,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub cr1: Option<ConditionRegister>,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub cr2: Option<ConditionRegister>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub cr3: Option<ConditionRegister>,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub cr4: Option<ConditionRegister>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub cr5: Option<ConditionRegister>,
165 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub cr6: Option<ConditionRegister>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub cr7: Option<ConditionRegister>,
169 }
170
171 #[derive(Debug)]
172 pub struct MissingInstructionInput {
173 pub input: InstructionInputRegister,
174 }
175
176 impl fmt::Display for MissingInstructionInput {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "missing instruction input: {}", self.input)
179 }
180 }
181
182 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
183 pub enum InstructionInputRegister {
184 #[serde(rename = "ra")]
185 Ra,
186 #[serde(rename = "rb")]
187 Rb,
188 #[serde(rename = "rc")]
189 Rc,
190 #[serde(rename = "ca")]
191 Carry,
192 }
193
194 forward_display_to_serde!(InstructionInputRegister);
195
196 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
197 pub struct InstructionInput {
198 #[serde(with = "serde_hex::SerdeHex")]
199 pub ra: u64,
200 #[serde(
201 default,
202 skip_serializing_if = "Option::is_none",
203 with = "serde_hex::SerdeHex"
204 )]
205 pub rb: Option<u64>,
206 #[serde(
207 default,
208 skip_serializing_if = "Option::is_none",
209 with = "serde_hex::SerdeHex"
210 )]
211 pub rc: Option<u64>,
212 #[serde(default, skip_serializing_if = "Option::is_none", flatten)]
213 pub carry: Option<CarryFlags>,
214 }
215
216 fn is_false(v: &bool) -> bool {
217 !v
218 }
219
220 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
221 pub struct TestCase {
222 pub instr: Instr,
223 #[serde(flatten)]
224 pub inputs: InstructionInput,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub native_outputs: Option<InstructionResult>,
227 pub model_outputs: InstructionResult,
228 #[serde(default, skip_serializing_if = "is_false")]
229 pub model_mismatch: bool,
230 }
231
232 #[derive(Clone, Debug, Serialize, Deserialize)]
233 pub struct WholeTest {
234 #[serde(default, skip_serializing_if = "Vec::is_empty")]
235 pub test_cases: Vec<TestCase>,
236 pub any_model_mismatch: bool,
237 }
238
239 #[cfg(feature = "native_instrs")]
240 macro_rules! map_instr_asm_args {
241 ([], [], []) => {
242 ""
243 };
244 ([], [], [$string0:literal $($strings:literal)*]) => {
245 concat!(" ", $string0, $(", ", $strings),*)
246 };
247 ([$($args:ident)*], [rt $($results:ident)*], [$($strings:literal)*]) => {
248 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$0"])
249 };
250 ([ra $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => {
251 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$3"])
252 };
253 ([rb $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => {
254 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$4"])
255 };
256 ([rc $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => {
257 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$5"])
258 };
259 ([$($args:ident)*], [ov $($results:ident)*], [$($strings:literal)*]) => {
260 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
261 };
262 ([$($args:ident)*], [cr0 $($results:ident)*], [$($strings:literal)*]) => {
263 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
264 };
265 ([$($args:ident)*], [cr1 $($results:ident)*], [$($strings:literal)*]) => {
266 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
267 };
268 ([$($args:ident)*], [cr2 $($results:ident)*], [$($strings:literal)*]) => {
269 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
270 };
271 ([$($args:ident)*], [cr3 $($results:ident)*], [$($strings:literal)*]) => {
272 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
273 };
274 ([$($args:ident)*], [cr4 $($results:ident)*], [$($strings:literal)*]) => {
275 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
276 };
277 ([$($args:ident)*], [cr5 $($results:ident)*], [$($strings:literal)*]) => {
278 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
279 };
280 ([$($args:ident)*], [cr6 $($results:ident)*], [$($strings:literal)*]) => {
281 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
282 };
283 ([$($args:ident)*], [cr7 $($results:ident)*], [$($strings:literal)*]) => {
284 map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*])
285 };
286 }
287
288 macro_rules! map_instr_input_registers {
289 ([], [$($reg:expr,)*]) => {
290 [$($reg,)*]
291 };
292 ([ra $($args:ident)*], [$($reg:expr,)*]) => {
293 map_instr_input_registers!([$($args)*], [InstructionInputRegister::Ra, $($reg,)*])
294 };
295 ([rb $($args:ident)*], [$($reg:expr,)*]) => {
296 map_instr_input_registers!([$($args)*], [InstructionInputRegister::Rb, $($reg,)*])
297 };
298 ([rc $($args:ident)*], [$($reg:expr,)*]) => {
299 map_instr_input_registers!([$($args)*], [InstructionInputRegister::Rc, $($reg,)*])
300 };
301 }
302
303 #[cfg(feature = "native_instrs")]
304 macro_rules! map_instr_results {
305 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, []) => {};
306 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [rt $($args:ident)*]) => {
307 $retval.rt = Some($rt);
308 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
309 };
310 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [ov $($args:ident)*]) => {
311 $retval.overflow = Some(OverflowFlags::from_xer($xer));
312 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
313 };
314 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr0 $($args:ident)*]) => {
315 $retval.cr0 = Some(ConditionRegister::from_cr_field($cr, 0));
316 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
317 };
318 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr1 $($args:ident)*]) => {
319 $retval.cr1 = Some(ConditionRegister::from_cr_field($cr, 1));
320 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
321 };
322 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr2 $($args:ident)*]) => {
323 $retval.cr2 = Some(ConditionRegister::from_cr_field($cr, 2));
324 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
325 };
326 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr3 $($args:ident)*]) => {
327 $retval.cr3 = Some(ConditionRegister::from_cr_field($cr, 3));
328 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
329 };
330 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr4 $($args:ident)*]) => {
331 $retval.cr4 = Some(ConditionRegister::from_cr_field($cr, 4));
332 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
333 };
334 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr5 $($args:ident)*]) => {
335 $retval.cr5 = Some(ConditionRegister::from_cr_field($cr, 5));
336 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
337 };
338 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr6 $($args:ident)*]) => {
339 $retval.cr6 = Some(ConditionRegister::from_cr_field($cr, 6));
340 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
341 };
342 ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr7 $($args:ident)*]) => {
343 $retval.cr7 = Some(ConditionRegister::from_cr_field($cr, 7));
344 map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]);
345 };
346 }
347
348 #[cfg(feature = "native_instrs")]
349 macro_rules! instr {
350 (
351 #[enumerant = $enumerant:ident]
352 fn $fn:ident($($args:ident),*) -> ($($results:ident),*) {
353 $instr:literal
354 }
355 ) => {
356 pub fn $fn(inputs: InstructionInput) -> InstructionResult {
357 #![allow(unused_variables, unused_assignments)]
358 let InstructionInput {
359 ra,
360 rb,
361 rc,
362 } = inputs;
363 let rt: u64;
364 let xer: u64;
365 let cr: u32;
366 unsafe {
367 llvm_asm!(
368 concat!(
369 "mfxer $1\n",
370 "and $1, $1, $7\n",
371 "mtxer $1\n",
372 $instr, " ",
373 map_instr_asm_args!([$($args)*], [$($results)*], []),
374 "\n",
375 "mfxer $1\n",
376 "mfcr $2\n",
377 )
378 : "=&b"(rt), "=&b"(xer), "=&b"(cr)
379 : "b"(ra), "b"(rb), "b"(rc), "b"(0u64), "b"(!0x8000_0000u64)
380 : "xer", "cr");
381 }
382 let mut retval = InstructionResult::default();
383 map_instr_results!(rt, xer, cr, retval, [$($results)*]);
384 retval
385 }
386 };
387 }
388
389 macro_rules! instrs {
390 (
391 $(
392 #[enumerant = $enumerant:ident]
393 fn $fn:ident($($args:ident),*) -> ($($results:ident),*) {
394 $instr:literal
395 }
396 )+
397 ) => {
398 #[cfg(feature = "python")]
399 macro_rules! wrap_all_instr_fns {
400 ($m:ident) => {
401 wrap_instr_fns! {
402 #![pymodule($m)]
403
404 $(fn $fn(inputs: InstructionInput) -> InstructionResult;)*
405 }
406 };
407 }
408
409 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
410 pub enum Instr {
411 $(
412 #[serde(rename = $instr)]
413 $enumerant,
414 )+
415 }
416
417 impl Instr {
418 #[cfg(feature = "native_instrs")]
419 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult {
420 match self {
421 $(
422 Self::$enumerant => native_instrs::$fn,
423 )+
424 }
425 }
426 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult {
427 match self {
428 $(
429 Self::$enumerant => instr_models::$fn,
430 )+
431 }
432 }
433 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
434 match self {
435 $(
436 Self::$enumerant => &map_instr_input_registers!([$($args)*], []),
437 )+
438 }
439 }
440 pub fn name(self) -> &'static str {
441 match self {
442 $(
443 Self::$enumerant => $instr,
444 )+
445 }
446 }
447 pub const VALUES: &'static [Self] = &[
448 $(
449 Self::$enumerant,
450 )+
451 ];
452 }
453
454 #[cfg(feature = "native_instrs")]
455 pub mod native_instrs {
456 use super::*;
457
458 $(
459 instr! {
460 #[enumerant = $enumerant]
461 fn $fn($($args),*) -> ($($results),*) {
462 $instr
463 }
464 }
465 )+
466 }
467 };
468 }
469
470 instrs! {
471 // add
472 #[enumerant = Add]
473 fn add(ra, rb) -> (rt) {
474 "add"
475 }
476 #[enumerant = AddO]
477 fn addo(ra, rb) -> (rt, ov) {
478 "addo"
479 }
480 #[enumerant = Add_]
481 fn add_(ra, rb) -> (rt, cr0) {
482 "add."
483 }
484 #[enumerant = AddO_]
485 fn addo_(ra, rb) -> (rt, ov, cr0) {
486 "addo."
487 }
488
489 // subf
490 #[enumerant = SubF]
491 fn subf(ra, rb) -> (rt) {
492 "subf"
493 }
494 #[enumerant = SubFO]
495 fn subfo(ra, rb) -> (rt, ov) {
496 "subfo"
497 }
498 #[enumerant = SubF_]
499 fn subf_(ra, rb) -> (rt, cr0) {
500 "subf."
501 }
502 #[enumerant = SubFO_]
503 fn subfo_(ra, rb) -> (rt, ov, cr0) {
504 "subfo."
505 }
506
507 // divde
508 #[enumerant = DivDE]
509 fn divde(ra, rb) -> (rt) {
510 "divde"
511 }
512 #[enumerant = DivDEO]
513 fn divdeo(ra, rb) -> (rt, ov) {
514 "divdeo"
515 }
516 #[enumerant = DivDE_]
517 fn divde_(ra, rb) -> (rt, cr0) {
518 "divde."
519 }
520 #[enumerant = DivDEO_]
521 fn divdeo_(ra, rb) -> (rt, ov, cr0) {
522 "divdeo."
523 }
524
525 // divdeu
526 #[enumerant = DivDEU]
527 fn divdeu(ra, rb) -> (rt) {
528 "divdeu"
529 }
530 #[enumerant = DivDEUO]
531 fn divdeuo(ra, rb) -> (rt, ov) {
532 "divdeuo"
533 }
534 #[enumerant = DivDEU_]
535 fn divdeu_(ra, rb) -> (rt, cr0) {
536 "divdeu."
537 }
538 #[enumerant = DivDEUO_]
539 fn divdeuo_(ra, rb) -> (rt, ov, cr0) {
540 "divdeuo."
541 }
542
543 // divd
544 #[enumerant = DivD]
545 fn divd(ra, rb) -> (rt) {
546 "divd"
547 }
548 #[enumerant = DivDO]
549 fn divdo(ra, rb) -> (rt, ov) {
550 "divdo"
551 }
552 #[enumerant = DivD_]
553 fn divd_(ra, rb) -> (rt, cr0) {
554 "divd."
555 }
556 #[enumerant = DivDO_]
557 fn divdo_(ra, rb) -> (rt, ov, cr0) {
558 "divdo."
559 }
560
561 // divdu
562 #[enumerant = DivDU]
563 fn divdu(ra, rb) -> (rt) {
564 "divdu"
565 }
566 #[enumerant = DivDUO]
567 fn divduo(ra, rb) -> (rt, ov) {
568 "divduo"
569 }
570 #[enumerant = DivDU_]
571 fn divdu_(ra, rb) -> (rt, cr0) {
572 "divdu."
573 }
574 #[enumerant = DivDUO_]
575 fn divduo_(ra, rb) -> (rt, ov, cr0) {
576 "divduo."
577 }
578
579 // divwe
580 #[enumerant = DivWE]
581 fn divwe(ra, rb) -> (rt) {
582 "divwe"
583 }
584 #[enumerant = DivWEO]
585 fn divweo(ra, rb) -> (rt, ov) {
586 "divweo"
587 }
588 #[enumerant = DivWE_]
589 fn divwe_(ra, rb) -> (rt, cr0) {
590 "divwe."
591 }
592 #[enumerant = DivWEO_]
593 fn divweo_(ra, rb) -> (rt, ov, cr0) {
594 "divweo."
595 }
596
597 // divweu
598 #[enumerant = DivWEU]
599 fn divweu(ra, rb) -> (rt) {
600 "divweu"
601 }
602 #[enumerant = DivWEUO]
603 fn divweuo(ra, rb) -> (rt, ov) {
604 "divweuo"
605 }
606 #[enumerant = DivWEU_]
607 fn divweu_(ra, rb) -> (rt, cr0) {
608 "divweu."
609 }
610 #[enumerant = DivWEUO_]
611 fn divweuo_(ra, rb) -> (rt, ov, cr0) {
612 "divweuo."
613 }
614
615 // divw
616 #[enumerant = DivW]
617 fn divw(ra, rb) -> (rt) {
618 "divw"
619 }
620 #[enumerant = DivWO]
621 fn divwo(ra, rb) -> (rt, ov) {
622 "divwo"
623 }
624 #[enumerant = DivW_]
625 fn divw_(ra, rb) -> (rt, cr0) {
626 "divw."
627 }
628 #[enumerant = DivWO_]
629 fn divwo_(ra, rb) -> (rt, ov, cr0) {
630 "divwo."
631 }
632
633 // divwu
634 #[enumerant = DivWU]
635 fn divwu(ra, rb) -> (rt) {
636 "divwu"
637 }
638 #[enumerant = DivWUO]
639 fn divwuo(ra, rb) -> (rt, ov) {
640 "divwuo"
641 }
642 #[enumerant = DivWU_]
643 fn divwu_(ra, rb) -> (rt, cr0) {
644 "divwu."
645 }
646 #[enumerant = DivWUO_]
647 fn divwuo_(ra, rb) -> (rt, ov, cr0) {
648 "divwuo."
649 }
650
651 // mod*
652 #[enumerant = ModSD]
653 fn modsd(ra, rb) -> (rt) {
654 "modsd"
655 }
656 #[enumerant = ModUD]
657 fn modud(ra, rb) -> (rt) {
658 "modud"
659 }
660 #[enumerant = ModSW]
661 fn modsw(ra, rb) -> (rt) {
662 "modsw"
663 }
664 #[enumerant = ModUW]
665 fn moduw(ra, rb) -> (rt) {
666 "moduw"
667 }
668
669 // mullw
670 #[enumerant = MulLW]
671 fn mullw(ra, rb) -> (rt) {
672 "mullw"
673 }
674 #[enumerant = MulLWO]
675 fn mullwo(ra, rb) -> (rt, ov) {
676 "mullwo"
677 }
678 #[enumerant = MulLW_]
679 fn mullw_(ra, rb) -> (rt, cr0) {
680 "mullw."
681 }
682 #[enumerant = MulLWO_]
683 fn mullwo_(ra, rb) -> (rt, ov, cr0) {
684 "mullwo."
685 }
686
687 // mulhw
688 #[enumerant = MulHW]
689 fn mulhw(ra, rb) -> (rt) {
690 "mulhw"
691 }
692 #[enumerant = MulHW_]
693 fn mulhw_(ra, rb) -> (rt, cr0) {
694 "mulhw."
695 }
696
697 // mulhwu
698 #[enumerant = MulHWU]
699 fn mulhwu(ra, rb) -> (rt) {
700 "mulhwu"
701 }
702 #[enumerant = MulHWU_]
703 fn mulhwu_(ra, rb) -> (rt, cr0) {
704 "mulhwu."
705 }
706
707 // mulld
708 #[enumerant = MulLD]
709 fn mulld(ra, rb) -> (rt) {
710 "mulld"
711 }
712 #[enumerant = MulLDO]
713 fn mulldo(ra, rb) -> (rt, ov) {
714 "mulldo"
715 }
716 #[enumerant = MulLD_]
717 fn mulld_(ra, rb) -> (rt, cr0) {
718 "mulld."
719 }
720 #[enumerant = MulLDO_]
721 fn mulldo_(ra, rb) -> (rt, ov, cr0) {
722 "mulldo."
723 }
724
725 // mulhd
726 #[enumerant = MulHD]
727 fn mulhd(ra, rb) -> (rt) {
728 "mulhd"
729 }
730 #[enumerant = MulHD_]
731 fn mulhd_(ra, rb) -> (rt, cr0) {
732 "mulhd."
733 }
734
735 // mulhdu
736 #[enumerant = MulHDU]
737 fn mulhdu(ra, rb) -> (rt) {
738 "mulhdu"
739 }
740 #[enumerant = MulHDU_]
741 fn mulhdu_(ra, rb) -> (rt, cr0) {
742 "mulhdu."
743 }
744
745 // madd*
746 #[enumerant = MAddHD]
747 fn maddhd(ra, rb, rc) -> (rt) {
748 "maddhd"
749 }
750 #[enumerant = MAddHDU]
751 fn maddhdu(ra, rb, rc) -> (rt) {
752 "maddhdu"
753 }
754 #[enumerant = MAddLD]
755 fn maddld(ra, rb, rc) -> (rt) {
756 "maddld"
757 }
758 }
759
760 // must be after instrs macro call since it uses a macro definition
761 mod python;
762 }
763
764 // must be after instrs macro call since it uses a macro definition
765 mod python;