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