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