working on adding proc-macro that replaces macro_rules-based instrs! macro
[power-instruction-analyzer.git] / src / instr_models.rs
1 use crate::{
2 ConditionRegister, InstructionInput, InstructionOutput, InstructionResult, OverflowFlags,
3 };
4
5 macro_rules! create_instr_variants_ov_cr {
6 ($fn:ident, $fno:ident, $fn_:ident, $fno_:ident, $iwidth:ident) => {
7 pub fn $fn(inputs: InstructionInput) -> InstructionResult {
8 Ok(InstructionOutput {
9 overflow: None,
10 ..$fno(inputs)?
11 })
12 }
13 pub fn $fn_(inputs: InstructionInput) -> InstructionResult {
14 let mut retval = $fno_(inputs)?;
15 let mut cr0 = retval.cr0.as_mut().expect("expected cr0 to be set");
16 cr0.so = false;
17 retval.overflow = None;
18 Ok(retval)
19 }
20 pub fn $fno_(inputs: InstructionInput) -> InstructionResult {
21 let mut retval = $fno(inputs)?;
22 let result = retval.rt.expect("expected rt to be set");
23 let so = retval.overflow.expect("expected overflow to be set").so;
24 let cr0 = ConditionRegister::from_signed_int(result as $iwidth, so);
25 retval.cr0 = Some(cr0);
26 Ok(retval)
27 }
28 };
29 }
30
31 macro_rules! create_instr_variants_cr {
32 ($fn:ident, $fn_:ident, $iwidth:ident) => {
33 pub fn $fn_(inputs: InstructionInput) -> InstructionResult {
34 let mut retval = $fn(inputs)?;
35 let result = retval.rt.expect("expected rt to be set");
36 let cr0 = ConditionRegister::from_signed_int(result as $iwidth, false);
37 retval.cr0 = Some(cr0);
38 Ok(retval)
39 }
40 };
41 }
42
43 create_instr_variants_ov_cr!(add, addo, add_, addo_, i64);
44
45 pub fn addo(inputs: InstructionInput) -> InstructionResult {
46 let ra = inputs.try_get_ra()? as i64;
47 let rb = inputs.try_get_rb()? as i64;
48 let (result, ov) = ra.overflowing_add(rb);
49 let result = result as u64;
50 let ov32 = (ra as i32).overflowing_add(rb as i32).1;
51 Ok(InstructionOutput {
52 rt: Some(result),
53 overflow: Some(OverflowFlags { so: ov, ov, ov32 }),
54 ..InstructionOutput::default()
55 })
56 }
57
58 create_instr_variants_ov_cr!(subf, subfo, subf_, subfo_, i64);
59
60 pub fn subfo(inputs: InstructionInput) -> InstructionResult {
61 let ra = inputs.try_get_ra()? as i64;
62 let rb = inputs.try_get_rb()? as i64;
63 let (result, ov) = rb.overflowing_sub(ra);
64 let result = result as u64;
65 let ov32 = (rb as i32).overflowing_sub(ra as i32).1;
66 Ok(InstructionOutput {
67 rt: Some(result),
68 overflow: Some(OverflowFlags { so: ov, ov, ov32 }),
69 ..InstructionOutput::default()
70 })
71 }
72
73 create_instr_variants_ov_cr!(divde, divdeo, divde_, divdeo_, i64);
74
75 pub fn divdeo(inputs: InstructionInput) -> InstructionResult {
76 let dividend = i128::from(inputs.try_get_ra()? as i64) << 64;
77 let divisor = i128::from(inputs.try_get_rb()? as i64);
78 let overflow;
79 let result;
80 if divisor == 0 || (divisor == -1 && dividend == i128::min_value()) {
81 result = 0;
82 overflow = true;
83 } else {
84 let result128 = dividend / divisor;
85 if result128 as i64 as i128 != result128 {
86 result = 0;
87 overflow = true;
88 } else {
89 result = result128 as u64;
90 overflow = false;
91 }
92 }
93 Ok(InstructionOutput {
94 rt: Some(result),
95 overflow: Some(OverflowFlags::from_overflow(overflow)),
96 ..InstructionOutput::default()
97 })
98 }
99
100 create_instr_variants_ov_cr!(divdeu, divdeuo, divdeu_, divdeuo_, i64);
101
102 pub fn divdeuo(inputs: InstructionInput) -> InstructionResult {
103 let dividend = u128::from(inputs.try_get_ra()?) << 64;
104 let divisor = u128::from(inputs.try_get_rb()?);
105 let overflow;
106 let result;
107 if divisor == 0 {
108 result = 0;
109 overflow = true;
110 } else {
111 let resultu128 = dividend / divisor;
112 if resultu128 > u128::from(u64::max_value()) {
113 result = 0;
114 overflow = true;
115 } else {
116 result = resultu128 as u64;
117 overflow = false;
118 }
119 }
120 Ok(InstructionOutput {
121 rt: Some(result),
122 overflow: Some(OverflowFlags::from_overflow(overflow)),
123 ..InstructionOutput::default()
124 })
125 }
126
127 create_instr_variants_ov_cr!(divd, divdo, divd_, divdo_, i64);
128
129 pub fn divdo(inputs: InstructionInput) -> InstructionResult {
130 let dividend = inputs.try_get_ra()? as i64;
131 let divisor = inputs.try_get_rb()? as i64;
132 let overflow;
133 let result;
134 if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
135 result = 0;
136 overflow = true;
137 } else {
138 result = (dividend / divisor) as u64;
139 overflow = false;
140 }
141 Ok(InstructionOutput {
142 rt: Some(result),
143 overflow: Some(OverflowFlags::from_overflow(overflow)),
144 ..InstructionOutput::default()
145 })
146 }
147
148 create_instr_variants_ov_cr!(divdu, divduo, divdu_, divduo_, i64);
149
150 pub fn divduo(inputs: InstructionInput) -> InstructionResult {
151 let dividend: u64 = inputs.try_get_ra()?;
152 let divisor: u64 = inputs.try_get_rb()?;
153 let overflow;
154 let result;
155 if divisor == 0 {
156 result = 0;
157 overflow = true;
158 } else {
159 result = dividend / divisor;
160 overflow = false;
161 }
162 Ok(InstructionOutput {
163 rt: Some(result),
164 overflow: Some(OverflowFlags::from_overflow(overflow)),
165 ..InstructionOutput::default()
166 })
167 }
168
169 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
170 create_instr_variants_ov_cr!(divwe, divweo, divwe_, divweo_, i64);
171
172 pub fn divweo(inputs: InstructionInput) -> InstructionResult {
173 let dividend = i64::from(inputs.try_get_ra()? as i32) << 32;
174 let divisor = i64::from(inputs.try_get_rb()? as i32);
175 let overflow;
176 let result;
177 if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
178 result = 0;
179 overflow = true;
180 } else {
181 let result64 = dividend / divisor;
182 if result64 as i32 as i64 != result64 {
183 result = 0;
184 overflow = true;
185 } else {
186 result = result64 as u32 as u64;
187 overflow = false;
188 }
189 }
190 Ok(InstructionOutput {
191 rt: Some(result),
192 overflow: Some(OverflowFlags::from_overflow(overflow)),
193 ..InstructionOutput::default()
194 })
195 }
196
197 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
198 create_instr_variants_ov_cr!(divweu, divweuo, divweu_, divweuo_, i64);
199
200 pub fn divweuo(inputs: InstructionInput) -> InstructionResult {
201 let dividend = u64::from(inputs.try_get_ra()? as u32) << 32;
202 let divisor = u64::from(inputs.try_get_rb()? as u32);
203 let overflow;
204 let result;
205 if divisor == 0 {
206 result = 0;
207 overflow = true;
208 } else {
209 let resultu64 = dividend / divisor;
210 if resultu64 > u64::from(u32::max_value()) {
211 result = 0;
212 overflow = true;
213 } else {
214 result = resultu64 as u32 as u64;
215 overflow = false;
216 }
217 }
218 Ok(InstructionOutput {
219 rt: Some(result),
220 overflow: Some(OverflowFlags::from_overflow(overflow)),
221 ..InstructionOutput::default()
222 })
223 }
224
225 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
226 create_instr_variants_ov_cr!(divw, divwo, divw_, divwo_, i64);
227
228 pub fn divwo(inputs: InstructionInput) -> InstructionResult {
229 let dividend = inputs.try_get_ra()? as i32;
230 let divisor = inputs.try_get_rb()? as i32;
231 let overflow;
232 let result;
233 if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) {
234 result = 0;
235 overflow = true;
236 } else {
237 result = (dividend / divisor) as u32 as u64;
238 overflow = false;
239 }
240 Ok(InstructionOutput {
241 rt: Some(result),
242 overflow: Some(OverflowFlags::from_overflow(overflow)),
243 ..InstructionOutput::default()
244 })
245 }
246
247 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
248 create_instr_variants_ov_cr!(divwu, divwuo, divwu_, divwuo_, i64);
249
250 pub fn divwuo(inputs: InstructionInput) -> InstructionResult {
251 let dividend = inputs.try_get_ra()? as u32;
252 let divisor = inputs.try_get_rb()? as u32;
253 let overflow;
254 let result;
255 if divisor == 0 {
256 result = 0;
257 overflow = true;
258 } else {
259 result = (dividend / divisor) as u64;
260 overflow = false;
261 }
262 Ok(InstructionOutput {
263 rt: Some(result),
264 overflow: Some(OverflowFlags::from_overflow(overflow)),
265 ..InstructionOutput::default()
266 })
267 }
268
269 pub fn modsd(inputs: InstructionInput) -> InstructionResult {
270 let dividend = inputs.try_get_ra()? as i64;
271 let divisor = inputs.try_get_rb()? as i64;
272 let result;
273 if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
274 result = 0;
275 } else {
276 result = (dividend % divisor) as u64;
277 }
278 Ok(InstructionOutput {
279 rt: Some(result),
280 ..InstructionOutput::default()
281 })
282 }
283
284 pub fn modud(inputs: InstructionInput) -> InstructionResult {
285 let dividend: u64 = inputs.try_get_ra()?;
286 let divisor: u64 = inputs.try_get_rb()?;
287 let result;
288 if divisor == 0 {
289 result = 0;
290 } else {
291 result = dividend % divisor;
292 }
293 Ok(InstructionOutput {
294 rt: Some(result),
295 ..InstructionOutput::default()
296 })
297 }
298
299 pub fn modsw(inputs: InstructionInput) -> InstructionResult {
300 let dividend = inputs.try_get_ra()? as i32;
301 let divisor = inputs.try_get_rb()? as i32;
302 let result;
303 if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) {
304 result = 0;
305 } else {
306 result = (dividend % divisor) as u64;
307 }
308 Ok(InstructionOutput {
309 rt: Some(result),
310 ..InstructionOutput::default()
311 })
312 }
313
314 pub fn moduw(inputs: InstructionInput) -> InstructionResult {
315 let dividend = inputs.try_get_ra()? as u32;
316 let divisor = inputs.try_get_rb()? as u32;
317 let result;
318 if divisor == 0 {
319 result = 0;
320 } else {
321 result = (dividend % divisor) as u64;
322 }
323 Ok(InstructionOutput {
324 rt: Some(result),
325 ..InstructionOutput::default()
326 })
327 }
328
329 create_instr_variants_ov_cr!(mullw, mullwo, mullw_, mullwo_, i64);
330
331 pub fn mullwo(inputs: InstructionInput) -> InstructionResult {
332 let ra = inputs.try_get_ra()? as i32 as i64;
333 let rb = inputs.try_get_rb()? as i32 as i64;
334 let result = ra.wrapping_mul(rb) as u64;
335 let overflow = result as i32 as i64 != result as i64;
336 Ok(InstructionOutput {
337 rt: Some(result),
338 overflow: Some(OverflowFlags::from_overflow(overflow)),
339 ..InstructionOutput::default()
340 })
341 }
342
343 create_instr_variants_cr!(mulhw, mulhw_, i32);
344
345 pub fn mulhw(inputs: InstructionInput) -> InstructionResult {
346 let ra = inputs.try_get_ra()? as i32 as i64;
347 let rb = inputs.try_get_rb()? as i32 as i64;
348 let result = (ra * rb) >> 32;
349 let mut result = result as u32 as u64;
350 result |= result << 32;
351 Ok(InstructionOutput {
352 rt: Some(result),
353 ..InstructionOutput::default()
354 })
355 }
356
357 create_instr_variants_cr!(mulhwu, mulhwu_, i32);
358
359 pub fn mulhwu(inputs: InstructionInput) -> InstructionResult {
360 let ra = inputs.try_get_ra()? as u32 as u64;
361 let rb = inputs.try_get_rb()? as u32 as u64;
362 let result = (ra * rb) >> 32;
363 let mut result = result as u32 as u64;
364 result |= result << 32;
365 Ok(InstructionOutput {
366 rt: Some(result),
367 ..InstructionOutput::default()
368 })
369 }
370
371 create_instr_variants_ov_cr!(mulld, mulldo, mulld_, mulldo_, i64);
372
373 pub fn mulldo(inputs: InstructionInput) -> InstructionResult {
374 let ra = inputs.try_get_ra()? as i64;
375 let rb = inputs.try_get_rb()? as i64;
376 let result = ra.wrapping_mul(rb) as u64;
377 let overflow = ra.checked_mul(rb).is_none();
378 Ok(InstructionOutput {
379 rt: Some(result),
380 overflow: Some(OverflowFlags::from_overflow(overflow)),
381 ..InstructionOutput::default()
382 })
383 }
384
385 create_instr_variants_cr!(mulhd, mulhd_, i64);
386
387 pub fn mulhd(inputs: InstructionInput) -> InstructionResult {
388 let ra = inputs.try_get_ra()? as i64 as i128;
389 let rb = inputs.try_get_rb()? as i64 as i128;
390 let result = ((ra * rb) >> 64) as i64;
391 let result = result as u64;
392 Ok(InstructionOutput {
393 rt: Some(result),
394 ..InstructionOutput::default()
395 })
396 }
397
398 create_instr_variants_cr!(mulhdu, mulhdu_, i64);
399
400 pub fn mulhdu(inputs: InstructionInput) -> InstructionResult {
401 let ra = inputs.try_get_ra()? as u128;
402 let rb = inputs.try_get_rb()? as u128;
403 let result = ((ra * rb) >> 64) as u64;
404 Ok(InstructionOutput {
405 rt: Some(result),
406 ..InstructionOutput::default()
407 })
408 }
409
410 pub fn maddhd(inputs: InstructionInput) -> InstructionResult {
411 let ra = inputs.try_get_ra()? as i64 as i128;
412 let rb = inputs.try_get_rb()? as i64 as i128;
413 let rc = inputs.try_get_rc()? as i64 as i128;
414 let result = ((ra * rb + rc) >> 64) as u64;
415 Ok(InstructionOutput {
416 rt: Some(result),
417 ..InstructionOutput::default()
418 })
419 }
420
421 pub fn maddhdu(inputs: InstructionInput) -> InstructionResult {
422 let ra = inputs.try_get_ra()? as u128;
423 let rb = inputs.try_get_rb()? as u128;
424 let rc = inputs.try_get_rc()? as u128;
425 let result = ((ra * rb + rc) >> 64) as u64;
426 Ok(InstructionOutput {
427 rt: Some(result),
428 ..InstructionOutput::default()
429 })
430 }
431
432 pub fn maddld(inputs: InstructionInput) -> InstructionResult {
433 let ra = inputs.try_get_ra()? as i64;
434 let rb = inputs.try_get_rb()? as i64;
435 let rc = inputs.try_get_rc()? as i64;
436 let result = ra.wrapping_mul(rb).wrapping_add(rc) as u64;
437 Ok(InstructionOutput {
438 rt: Some(result),
439 ..InstructionOutput::default()
440 })
441 }