3647f97364bd85895e5695f9874edf6713b757d6
[power-instruction-analyzer.git] / src / main.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
3
4 #![feature(llvm_asm)]
5
6 mod serde_hex;
7
8 use serde::{Deserialize, Serialize};
9
10 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
11 pub struct OverflowFlags {
12 pub overflow: bool,
13 pub overflow32: bool,
14 }
15
16 impl OverflowFlags {
17 pub fn from_xer(xer: u64) -> Self {
18 Self {
19 overflow: (xer & 0x4000_0000) != 0,
20 overflow32: (xer & 0x8_0000) != 0,
21 }
22 }
23 }
24
25 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
26 pub struct TestDivResult {
27 #[serde(with = "serde_hex::SerdeHex")]
28 pub result: u64,
29 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
30 pub overflow: Option<OverflowFlags>,
31 }
32
33 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
34 pub struct TestDivInput {
35 #[serde(with = "serde_hex::SerdeHex")]
36 pub dividend: u64,
37 #[serde(with = "serde_hex::SerdeHex")]
38 pub divisor: u64,
39 #[serde(with = "serde_hex::SerdeHex")]
40 pub result_prev: u64,
41 }
42
43 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
44 pub struct TestDivCase {
45 pub instr: TestDivInstr,
46 #[serde(flatten)]
47 pub inputs: TestDivInput,
48 #[serde(flatten)]
49 pub outputs: TestDivResult,
50 }
51
52 macro_rules! make_div_functions {
53 (
54 #[div]
55 {
56 $($div_enum:ident = $div_fn:ident ($div_instr:literal),)+
57 }
58 #[rem]
59 {
60 $($rem_enum:ident = $rem_fn:ident ($rem_instr:literal),)+
61 }
62 ) => {
63 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
64 pub enum TestDivInstr {
65 $(
66 #[serde(rename = $div_instr)]
67 $div_enum,
68 )+
69 $(
70 #[serde(rename = $rem_instr)]
71 $rem_enum,
72 )+
73 }
74
75 impl TestDivInstr {
76 pub fn get_fn(self) -> fn(TestDivInput) -> TestDivResult {
77 match self {
78 $(
79 Self::$div_enum => TestDivInput::$div_fn,
80 )+
81 $(
82 Self::$rem_enum => TestDivInput::$rem_fn,
83 )+
84 }
85 }
86 pub fn name(self) -> &'static str {
87 match self {
88 $(
89 Self::$div_enum => $div_instr,
90 )+
91 $(
92 Self::$rem_enum => $rem_instr,
93 )+
94 }
95 }
96 pub const VALUES: &'static [Self] = &[
97 $(
98 Self::$div_enum,
99 )+
100 $(
101 Self::$rem_enum,
102 )+
103 ];
104 }
105
106 impl TestDivInput {
107 $(
108 pub fn $div_fn(self) -> TestDivResult {
109 let Self {
110 dividend,
111 divisor,
112 result_prev,
113 } = self;
114 let result: u64;
115 let xer: u64;
116 unsafe {
117 llvm_asm!(
118 concat!(
119 $div_instr,
120 " $0, $3, $4\n",
121 "mfxer $1"
122 )
123 : "=&r"(result), "=&r"(xer)
124 : "0"(result_prev), "r"(dividend), "r"(divisor)
125 : "xer");
126 }
127 TestDivResult {
128 result,
129 overflow: Some(OverflowFlags::from_xer(xer)),
130 }
131 }
132 )+
133 $(
134 pub fn $rem_fn(self) -> TestDivResult {
135 let Self {
136 dividend,
137 divisor,
138 result_prev,
139 } = self;
140 let result: u64;
141 unsafe {
142 llvm_asm!(
143 concat!(
144 $rem_instr,
145 " $0, $2, $3"
146 )
147 : "=&r"(result)
148 : "0"(result_prev), "r"(dividend), "r"(divisor));
149 }
150 TestDivResult {
151 result,
152 overflow: None,
153 }
154 }
155 )+
156 }
157 };
158 }
159
160 make_div_functions! {
161 #[div]
162 {
163 DivDE = divde("divdeo"),
164 DivDEU = divdeu("divdeuo"),
165 DivD = divd("divdo"),
166 DivDU = divdu("divduo"),
167 DivWE = divwe("divweo"),
168 DivWEU = divweu("divweuo"),
169 DivW = divw("divwo"),
170 DivWU = divwu("divwuo"),
171 }
172 #[rem]
173 {
174 ModSD = modsd("modsd"),
175 ModUD = modud("modud"),
176 ModSW = modsw("modsw"),
177 ModUW = moduw("moduw"),
178 }
179 }
180
181 const TEST_VALUES: &[u64] = &[
182 0x0,
183 0xFFFF_FFFF_FFFF_FFFF,
184 0x7FFF_FFFF_FFFF_FFFF,
185 0x8000_0000_0000_0000,
186 0x1234_5678_0000_0000,
187 0x1234_5678_8000_0000,
188 0x1234_5678_FFFF_FFFF,
189 0x1234_5678_7FFF_FFFF,
190 ];
191
192 fn main() {
193 let mut cases = Vec::new();
194 for &instr in TestDivInstr::VALUES {
195 for &dividend in TEST_VALUES {
196 for &divisor in TEST_VALUES {
197 let inputs = TestDivInput {
198 dividend,
199 divisor,
200 result_prev: 0xFECD_BA98_7654_3210,
201 };
202 let outputs = instr.get_fn()(inputs);
203 cases.push(TestDivCase {
204 instr,
205 inputs,
206 outputs,
207 });
208 }
209 }
210 }
211 serde_json::to_writer_pretty(std::io::stdout().lock(), &cases).unwrap();
212 }