add more test cases
[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 #![cfg_attr(feature = "native_instrs", feature(llvm_asm))]
5
6 mod instr_models;
7 mod serde_hex;
8
9 use serde::{Deserialize, Serialize};
10
11 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
12 pub struct OverflowFlags {
13 pub overflow: bool,
14 pub overflow32: bool,
15 }
16
17 impl OverflowFlags {
18 pub fn from_xer(xer: u64) -> Self {
19 Self {
20 overflow: (xer & 0x4000_0000) != 0,
21 overflow32: (xer & 0x8_0000) != 0,
22 }
23 }
24 }
25
26 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
27 pub struct TestDivResult {
28 #[serde(with = "serde_hex::SerdeHex")]
29 pub result: u64,
30 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
31 pub overflow: Option<OverflowFlags>,
32 }
33
34 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
35 pub struct TestDivInput {
36 #[serde(with = "serde_hex::SerdeHex")]
37 pub dividend: u64,
38 #[serde(with = "serde_hex::SerdeHex")]
39 pub divisor: u64,
40 #[serde(with = "serde_hex::SerdeHex")]
41 pub result_prev: u64,
42 }
43
44 fn is_false(v: &bool) -> bool {
45 !v
46 }
47
48 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
49 pub struct TestDivCase {
50 pub instr: TestDivInstr,
51 #[serde(flatten)]
52 pub inputs: TestDivInput,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub native_outputs: Option<TestDivResult>,
55 pub model_outputs: TestDivResult,
56 #[serde(default, skip_serializing_if = "is_false")]
57 pub model_mismatch: bool,
58 }
59
60 #[derive(Clone, Debug, Serialize, Deserialize)]
61 pub struct WholeTest {
62 #[serde(default, skip_serializing_if = "Vec::is_empty")]
63 pub test_div_cases: Vec<TestDivCase>,
64 pub any_model_mismatch: bool,
65 }
66
67 macro_rules! make_div_functions {
68 (
69 #[div]
70 {
71 $($div_enum:ident = $div_fn:ident ($div_instr:literal),)+
72 }
73 #[rem]
74 {
75 $($rem_enum:ident = $rem_fn:ident ($rem_instr:literal),)+
76 }
77 ) => {
78 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
79 pub enum TestDivInstr {
80 $(
81 #[serde(rename = $div_instr)]
82 $div_enum,
83 )+
84 $(
85 #[serde(rename = $rem_instr)]
86 $rem_enum,
87 )+
88 }
89
90 impl TestDivInstr {
91 #[cfg(feature = "native_instrs")]
92 pub fn get_native_fn(self) -> fn(TestDivInput) -> TestDivResult {
93 match self {
94 $(
95 Self::$div_enum => native_instrs::$div_fn,
96 )+
97 $(
98 Self::$rem_enum => native_instrs::$rem_fn,
99 )+
100 }
101 }
102 pub fn get_model_fn(self) -> fn(TestDivInput) -> TestDivResult {
103 match self {
104 $(
105 Self::$div_enum => instr_models::$div_fn,
106 )+
107 $(
108 Self::$rem_enum => instr_models::$rem_fn,
109 )+
110 }
111 }
112 pub fn name(self) -> &'static str {
113 match self {
114 $(
115 Self::$div_enum => $div_instr,
116 )+
117 $(
118 Self::$rem_enum => $rem_instr,
119 )+
120 }
121 }
122 pub const VALUES: &'static [Self] = &[
123 $(
124 Self::$div_enum,
125 )+
126 $(
127 Self::$rem_enum,
128 )+
129 ];
130 }
131
132 #[cfg(feature = "native_instrs")]
133 mod native_instrs {
134 use super::*;
135
136 $(
137 pub fn $div_fn(inputs: TestDivInput) -> TestDivResult {
138 let TestDivInput {
139 dividend,
140 divisor,
141 result_prev,
142 } = inputs;
143 let result: u64;
144 let xer: u64;
145 unsafe {
146 llvm_asm!(
147 concat!(
148 $div_instr,
149 " $0, $3, $4\n",
150 "mfxer $1"
151 )
152 : "=&r"(result), "=&r"(xer)
153 : "0"(result_prev), "r"(dividend), "r"(divisor)
154 : "xer");
155 }
156 TestDivResult {
157 result,
158 overflow: Some(OverflowFlags::from_xer(xer)),
159 }
160 }
161 )+
162 $(
163 pub fn $rem_fn(inputs: TestDivInput) -> TestDivResult {
164 let TestDivInput {
165 dividend,
166 divisor,
167 result_prev,
168 } = inputs;
169 let result: u64;
170 unsafe {
171 llvm_asm!(
172 concat!(
173 $rem_instr,
174 " $0, $2, $3"
175 )
176 : "=&r"(result)
177 : "0"(result_prev), "r"(dividend), "r"(divisor));
178 }
179 TestDivResult {
180 result,
181 overflow: None,
182 }
183 }
184 )+
185 }
186 };
187 }
188
189 make_div_functions! {
190 #[div]
191 {
192 DivDEO = divdeo("divdeo"),
193 DivDEUO = divdeuo("divdeuo"),
194 DivDO = divdo("divdo"),
195 DivDUO = divduo("divduo"),
196 DivWEO = divweo("divweo"),
197 DivWEUO = divweuo("divweuo"),
198 DivWO = divwo("divwo"),
199 DivWUO = divwuo("divwuo"),
200 }
201 #[rem]
202 {
203 ModSD = modsd("modsd"),
204 ModUD = modud("modud"),
205 ModSW = modsw("modsw"),
206 ModUW = moduw("moduw"),
207 }
208 }
209
210 const TEST_VALUES: &[u64] = &[
211 0x0,
212 0x1,
213 0x2,
214 0xFFFF_FFFF_FFFF_FFFF,
215 0xFFFF_FFFF_FFFF_FFFE,
216 0x7FFF_FFFF_FFFF_FFFF,
217 0x8000_0000_0000_0000,
218 0x1234_5678_0000_0000,
219 0x1234_5678_8000_0000,
220 0x1234_5678_FFFF_FFFF,
221 0x1234_5678_7FFF_FFFF,
222 ];
223
224 fn main() {
225 let mut test_div_cases = Vec::new();
226 let mut any_model_mismatch = false;
227 for &instr in TestDivInstr::VALUES {
228 for &dividend in TEST_VALUES {
229 for &divisor in TEST_VALUES {
230 let inputs = TestDivInput {
231 dividend,
232 divisor,
233 result_prev: 0xFECD_BA98_7654_3210,
234 };
235 let model_outputs = instr.get_model_fn()(inputs);
236 #[cfg(feature = "native_instrs")]
237 let native_outputs = Some(instr.get_native_fn()(inputs));
238 #[cfg(not(feature = "native_instrs"))]
239 let native_outputs = None;
240 let model_mismatch = match native_outputs {
241 Some(native_outputs) if native_outputs != model_outputs => true,
242 _ => false,
243 };
244 any_model_mismatch |= model_mismatch;
245 test_div_cases.push(TestDivCase {
246 instr,
247 inputs,
248 native_outputs,
249 model_outputs,
250 model_mismatch,
251 });
252 }
253 }
254 }
255 let whole_test = WholeTest {
256 test_div_cases,
257 any_model_mismatch,
258 };
259 serde_json::to_writer_pretty(std::io::stdout().lock(), &whole_test).unwrap();
260 println!();
261 }