d6a8082c4a8a2fa5a08e4c0d635f3e8f9d51d07c
[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 python;
11 mod serde_hex;
12
13 use serde::{Deserialize, Serialize};
14
15 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
16 pub struct OverflowFlags {
17 pub overflow: bool,
18 pub overflow32: bool,
19 }
20
21 impl OverflowFlags {
22 pub fn from_xer(xer: u64) -> Self {
23 Self {
24 overflow: (xer & 0x4000_0000) != 0,
25 overflow32: (xer & 0x8_0000) != 0,
26 }
27 }
28 }
29
30 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
31 pub struct DivResult {
32 #[serde(with = "serde_hex::SerdeHex")]
33 pub result: u64,
34 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
35 pub overflow: Option<OverflowFlags>,
36 }
37
38 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
39 pub struct DivInput {
40 #[serde(with = "serde_hex::SerdeHex")]
41 pub dividend: u64,
42 #[serde(with = "serde_hex::SerdeHex")]
43 pub divisor: u64,
44 #[serde(default, with = "serde_hex::SerdeHex")]
45 pub result_prev: u64,
46 }
47
48 fn is_false(v: &bool) -> bool {
49 !v
50 }
51
52 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
53 pub struct TestDivCase {
54 pub instr: DivInstr,
55 #[serde(flatten)]
56 pub inputs: DivInput,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub native_outputs: Option<DivResult>,
59 pub model_outputs: DivResult,
60 #[serde(default, skip_serializing_if = "is_false")]
61 pub model_mismatch: bool,
62 }
63
64 #[derive(Clone, Debug, Serialize, Deserialize)]
65 pub struct WholeTest {
66 #[serde(default, skip_serializing_if = "Vec::is_empty")]
67 pub test_div_cases: Vec<TestDivCase>,
68 pub any_model_mismatch: bool,
69 }
70
71 macro_rules! make_div_functions {
72 (
73 #[div]
74 {
75 $($div_enum:ident = $div_fn:ident ($div_instr:literal),)+
76 }
77 #[rem]
78 {
79 $($rem_enum:ident = $rem_fn:ident ($rem_instr:literal),)+
80 }
81 ) => {
82 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
83 pub enum DivInstr {
84 $(
85 #[serde(rename = $div_instr)]
86 $div_enum,
87 )+
88 $(
89 #[serde(rename = $rem_instr)]
90 $rem_enum,
91 )+
92 }
93
94 impl DivInstr {
95 #[cfg(feature = "native_instrs")]
96 pub fn get_native_fn(self) -> fn(DivInput) -> DivResult {
97 match self {
98 $(
99 Self::$div_enum => native_instrs::$div_fn,
100 )+
101 $(
102 Self::$rem_enum => native_instrs::$rem_fn,
103 )+
104 }
105 }
106 pub fn get_model_fn(self) -> fn(DivInput) -> DivResult {
107 match self {
108 $(
109 Self::$div_enum => instr_models::$div_fn,
110 )+
111 $(
112 Self::$rem_enum => instr_models::$rem_fn,
113 )+
114 }
115 }
116 pub fn name(self) -> &'static str {
117 match self {
118 $(
119 Self::$div_enum => $div_instr,
120 )+
121 $(
122 Self::$rem_enum => $rem_instr,
123 )+
124 }
125 }
126 pub const VALUES: &'static [Self] = &[
127 $(
128 Self::$div_enum,
129 )+
130 $(
131 Self::$rem_enum,
132 )+
133 ];
134 }
135
136 #[cfg(feature = "native_instrs")]
137 pub mod native_instrs {
138 use super::*;
139
140 $(
141 pub fn $div_fn(inputs: DivInput) -> DivResult {
142 let DivInput {
143 dividend,
144 divisor,
145 result_prev,
146 } = inputs;
147 let result: u64;
148 let xer: u64;
149 unsafe {
150 llvm_asm!(
151 concat!(
152 $div_instr,
153 " $0, $3, $4\n",
154 "mfxer $1"
155 )
156 : "=&r"(result), "=&r"(xer)
157 : "0"(result_prev), "r"(dividend), "r"(divisor)
158 : "xer");
159 }
160 DivResult {
161 result,
162 overflow: Some(OverflowFlags::from_xer(xer)),
163 }
164 }
165 )+
166 $(
167 pub fn $rem_fn(inputs: DivInput) -> DivResult {
168 let DivInput {
169 dividend,
170 divisor,
171 result_prev,
172 } = inputs;
173 let result: u64;
174 unsafe {
175 llvm_asm!(
176 concat!(
177 $rem_instr,
178 " $0, $2, $3"
179 )
180 : "=&r"(result)
181 : "0"(result_prev), "r"(dividend), "r"(divisor));
182 }
183 DivResult {
184 result,
185 overflow: None,
186 }
187 }
188 )+
189 }
190 };
191 }
192
193 make_div_functions! {
194 #[div]
195 {
196 DivDEO = divdeo("divdeo"),
197 DivDEUO = divdeuo("divdeuo"),
198 DivDO = divdo("divdo"),
199 DivDUO = divduo("divduo"),
200 DivWEO = divweo("divweo"),
201 DivWEUO = divweuo("divweuo"),
202 DivWO = divwo("divwo"),
203 DivWUO = divwuo("divwuo"),
204 }
205 #[rem]
206 {
207 ModSD = modsd("modsd"),
208 ModUD = modud("modud"),
209 ModSW = modsw("modsw"),
210 ModUW = moduw("moduw"),
211 }
212 }