From fc8a6cd95974c5434c4f1fc243678d6ec31df738 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 23 Jan 2026 09:38:45 -0800 Subject: [PATCH] split up tests/simple_power_isa_decoder into separate modules --- .../tests/simple_power_isa_decoder/main.rs | 1397 +---------------- .../simple_power_isa_decoder/test_cases.rs | 119 ++ .../test_cases/branch.rs | 446 ++++++ .../test_cases/fixed_point_arithmetic.rs | 408 +++++ .../test_cases/fixed_point_compare.rs | 163 ++ .../test_cases/fixed_point_logical.rs | 273 ++++ .../move_to_from_system_register.rs | 37 + .../test_cases/prefixed_no_operation.rs | 16 + 8 files changed, 1468 insertions(+), 1391 deletions(-) create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_arithmetic.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_compare.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_logical.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/move_to_from_system_register.rs create mode 100644 crates/cpu/tests/simple_power_isa_decoder/test_cases/prefixed_no_operation.rs diff --git a/crates/cpu/tests/simple_power_isa_decoder/main.rs b/crates/cpu/tests/simple_power_isa_decoder/main.rs index 2d7302f..8c7a19f 100644 --- a/crates/cpu/tests/simple_power_isa_decoder/main.rs +++ b/crates/cpu/tests/simple_power_isa_decoder/main.rs @@ -1,1399 +1,14 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use crate::test_cases::TestCase; use cpu::{ - decoder::simple_power_isa::decode_one_insn, - instruction::{ - AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LogicalFlagsMOp, - LogicalFlagsMOpImm, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, MoveRegMOp, - OutputIntegerMode, - }, - util::array_vec::ArrayVec, + decoder::simple_power_isa::decode_one_insn, instruction::MOp, util::array_vec::ArrayVec, }; use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter}; -use std::{ - fmt::{self, Write as _}, - io::Write, - process::Command, -}; +use std::{fmt::Write as _, io::Write, process::Command}; -struct TestCase { - mnemonic: &'static str, - first_input: u32, - second_input: Option, - output: SimValue>>, - loc: &'static std::panic::Location<'static>, -} - -impl fmt::Debug for TestCase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - mnemonic, - first_input, - second_input, - output, - loc, - } = self; - let mut debug_struct = f.debug_struct("TestCase"); - debug_struct - .field("mnemonic", mnemonic) - .field("first_input", &format_args!("0x{first_input:08x}")); - if let Some(second_input) = second_input { - debug_struct.field("second_input", &format_args!("0x{second_input:08x}")); - } else { - debug_struct.field("second_input", &format_args!("None")); - } - debug_struct - .field("output", &ArrayVec::elements_sim_ref(output)) - .field("loc", &format_args!("{loc}")) - .finish() - } -} - -fn test_cases() -> Vec { - let mut retval = Vec::new(); - #[track_caller] - fn insn_empty(mnemonic: &'static str, first_input: u32, second_input: Option) -> TestCase { - let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) - .zero() - .cast_bits_to(MOp); - TestCase { - mnemonic, - first_input, - second_input, - output: ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop), - loc: std::panic::Location::caller(), - } - } - #[track_caller] - fn insn_single( - mnemonic: &'static str, - first_input: u32, - second_input: Option, - output: impl ToSimValue, - ) -> TestCase { - let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) - .zero() - .cast_bits_to(MOp); - let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop); - ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space"); - ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value(); - TestCase { - mnemonic, - first_input, - second_input, - output: single_storage, - loc: std::panic::Location::caller(), - } - } - #[track_caller] - fn insn_double( - mnemonic: &'static str, - first_input: u32, - second_input: Option, - insns: [impl ToSimValue; 2], - ) -> TestCase { - let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) - .zero() - .cast_bits_to(MOp); - let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop); - ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space"); - ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space"); - ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value(); - ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value(); - TestCase { - mnemonic, - first_input, - second_input, - output: single_storage, - loc: std::panic::Location::caller(), - } - } - retval.push(insn_single( - "addi 3, 4, 0x1234", - 0x38641234, - None, - AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - ], - 0x1234.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "paddi 3, 4, 0x123456789, 0", - 0x06012345, - Some(0x38646789), - AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - ], - 0x123456789i64.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "paddi 3, 0, 0x123456789, 1", - 0x06112345, - Some(0x38606789), - AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [MOpRegNum::const_zero().value, MOpRegNum::const_zero().value], - 0x123456789i64.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - true, - ), - )); - retval.push(insn_single( - "addis 3, 4, 0x1234", - 0x3C641234, - None, - AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - ], - 0x12340000.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "addpcis 3, 0x1234", - 0x4c7a1204, - None, - AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [MOpRegNum::const_zero().value; _], - 0x12340004.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - true, - ), - )); - retval.push(insn_single( - "add. 3, 4, 5", - 0x7c642a15, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num(3)], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "addic. 3, 4, 0x1234", - 0x34641234, - None, - AddSubMOp::add_sub_i( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - ], - 0x1234.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "subf. 3, 4, 5", - 0x7c642851, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num(3)], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - false, - true, - false, - ), - )); - retval.push(insn_single( - "subfic 3, 4, 0x1234", - 0x20641234, - None, - AddSubMOp::add_sub_i( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - ], - 0x1234.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - false, - true, - false, - ), - )); - retval.push(insn_single( - "addc. 3, 4, 5", - 0x7c642815, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ), - )); - retval.push(insn_single( - "subfc. 3, 4, 5", - 0x7c642811, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - false, - true, - false, - ), - )); - retval.push(insn_single( - "adde. 3, 4, 5", - 0x7c642915, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - true, - false, - false, - ), - )); - retval.push(insn_single( - "subfe. 3, 4, 5", - 0x7c642911, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - true, - false, - false, - ), - )); - retval.push(insn_single( - "addme. 3, 4", - 0x7c6401d5, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::const_zero().value, - ], - (-1i8).cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - true, - false, - false, - ), - )); - retval.push(insn_single( - "subfme. 3, 4", - 0x7c6401d1, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::const_zero().value, - ], - (-1i8).cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - true, - false, - false, - ), - )); - retval.push(insn_single( - "addze. 3, 4", - 0x7c640195, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - true, - false, - false, - ), - )); - retval.push(insn_single( - "subfze. 3, 4", - 0x7c640191, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[ - MOpRegNum::power_isa_gpr_reg_num(3), - MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, - ], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - true, - false, - false, - ), - )); - retval.push(insn_single( - "neg. 3, 4", - 0x7c6400d1, - None, - AddSubMOp::add_sub( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num(3)], - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], - ), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::const_zero().value, - MOpRegNum::const_zero().value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - true, - false, - true, - false, - ), - )); - retval.push(insn_single( - "cmpi 3, 0, 4, 0x1234", - 0x2d841234, - None, - CompareMOp::compare_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [MOpRegNum::power_isa_gpr_reg_imm(4).value], - 0x1234.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.S32(), - ), - )); - retval.push(insn_single( - "cmpi 3, 1, 4, -0x7655", - 0x2da489ab, - None, - CompareMOp::compare_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [MOpRegNum::power_isa_gpr_reg_imm(4).value], - (0x89abu16 as i16).cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.S64(), - ), - )); - retval.push(insn_single( - "cmp 3, 0, 4, 5", - 0x7d842800, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.S32(), - ), - )); - retval.push(insn_single( - "cmp 3, 1, 4, 5", - 0x7da42800, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.S64(), - ), - )); - retval.push(insn_single( - "cmpli 3, 0, 4, 0x1234", - 0x29841234, - None, - CompareMOp::compare_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [MOpRegNum::power_isa_gpr_reg_imm(4).value], - 0x1234.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.U32(), - ), - )); - retval.push(insn_single( - "cmpli 3, 1, 4, 0x89ab", - 0x29a489ab, - None, - CompareMOp::compare_i( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [MOpRegNum::power_isa_gpr_reg_imm(4).value], - 0x89ab.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.U64(), - ), - )); - retval.push(insn_single( - "cmpl 3, 0, 4, 5", - 0x7d842840, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.U32(), - ), - )); - retval.push(insn_single( - "cmpl 3, 1, 4, 5", - 0x7da42840, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.U64(), - ), - )); - retval.push(insn_single( - "cmprb 3, 0, 4, 5", - 0x7d842980, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.CmpRBOne(), - ), - )); - retval.push(insn_single( - "cmprb 3, 1, 4, 5", - 0x7da42980, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.CmpRBTwo(), - ), - )); - retval.push(insn_single( - "cmpeqb 3, 4, 5", - 0x7d8429c0, - None, - CompareMOp::compare( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_gpr_reg_imm(4).value, - MOpRegNum::power_isa_gpr_reg_imm(5).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - CompareMode.CmpEqB(), - ), - )); - macro_rules! insn_logic_i { - ( - $mnemonic:literal $dest:literal, $src:literal, $imm:literal; - $encoding:literal; - |$a:ident, $b:ident| $lut_fn:expr; - ) => { - retval.push(insn_single( - concat!( - $mnemonic, - " ", - stringify!($dest), - ", ", - stringify!($src), - ", ", - stringify!($imm) - ), - $encoding, - None, - LogicalMOp::logical_i( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num($dest)], - if $mnemonic.contains('.') { - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] - } else { - &[] - }, - ), - [MOpRegNum::power_isa_gpr_reg_imm($src).value], - (($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 }) - .cast_to_static::>(), - OutputIntegerMode.Full64(), - Lut4::from_fn(|$a, $b| $lut_fn), - ), - )); - }; - } - insn_logic_i! { - "andi." 3, 4, 0x89ab; - 0x708389ab; - |a, b| a & b; - } - insn_logic_i! { - "andis." 3, 4, 0x89ab; - 0x748389ab; - |a, b| a & b; - } - insn_logic_i! { - "ori" 3, 4, 0x89ab; - 0x608389ab; - |a, b| a | b; - } - // ensure nop decodes to zero instructions - retval.push(insn_empty("ori 0, 0, 0", 0x60000000, None)); - insn_logic_i! { - "oris" 3, 4, 0x89ab; - 0x648389ab; - |a, b| a | b; - } - insn_logic_i! { - "xori" 3, 4, 0x89ab; - 0x688389ab; - |a, b| a ^ b; - } - insn_logic_i! { - "xori" 0, 0, 0; // ensure xnop actually decodes to a normal ALU instruction - 0x68000000; - |a, b| a ^ b; - } - insn_logic_i! { - "xoris" 3, 4, 0x89ab; - 0x6c8389ab; - |a, b| a ^ b; - } - macro_rules! insn_logic { - ( - $mnemonic:literal $dest:literal, $src0:literal, $src1:literal; - $encoding:literal; - |$a:ident, $b:ident| $lut_fn:expr; - ) => { - retval.push(insn_single( - concat!( - $mnemonic, - " ", - stringify!($dest), - ", ", - stringify!($src0), - ", ", - stringify!($src1) - ), - $encoding, - None, - LogicalMOp::logical( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num($dest)], - if $mnemonic.contains('.') { - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] - } else { - &[] - }, - ), - [ - MOpRegNum::power_isa_gpr_reg_imm($src0).value, - MOpRegNum::power_isa_gpr_reg_imm($src1).value, - ], - 0.cast_to_static::>(), - OutputIntegerMode.Full64(), - Lut4::from_fn(|$a, $b| $lut_fn), - ), - )); - }; - } - insn_logic! { - "and" 3, 4, 5; - 0x7c832838; - |a, b| a & b; - } - insn_logic! { - "and." 3, 4, 5; - 0x7c832839; - |a, b| a & b; - } - insn_logic! { - "xor" 3, 4, 5; - 0x7c832a78; - |a, b| a ^ b; - } - insn_logic! { - "xor." 3, 4, 5; - 0x7c832a79; - |a, b| a ^ b; - } - insn_logic! { - "nand" 3, 4, 5; - 0x7c832bb8; - |a, b| !(a & b); - } - insn_logic! { - "nand." 3, 4, 5; - 0x7c832bb9; - |a, b| !(a & b); - } - insn_logic! { - "or" 3, 4, 5; - 0x7c832b78; - |a, b| a | b; - } - retval.push(insn_single( - "or 3, 4, 4", // mr 3, 4 - 0x7c832378, - None, - MoveRegMOp::move_reg( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), - [MOpRegNum::power_isa_gpr_reg_imm(4).value], - 0.cast_to_static::>(), - ), - )); - insn_logic! { - "or." 3, 4, 5; - 0x7c832b79; - |a, b| a | b; - } - insn_logic! { - "or." 3, 4, 4; // mr. 3, 4 - 0x7c832379; - |a, b| a | b; - } - insn_logic! { - "orc" 3, 4, 5; - 0x7c832b38; - |a, b| a | !b; - } - insn_logic! { - "orc." 3, 4, 5; - 0x7c832b39; - |a, b| a | !b; - } - insn_logic! { - "nor" 3, 4, 5; - 0x7c8328f8; - |a, b| !(a | b); - } - insn_logic! { - "nor." 3, 4, 5; - 0x7c8328f9; - |a, b| !(a | b); - } - insn_logic! { - "eqv" 3, 4, 5; - 0x7c832a38; - |a, b| a == b; - } - insn_logic! { - "eqv." 3, 4, 5; - 0x7c832a39; - |a, b| a == b; - } - insn_logic! { - "andc" 3, 4, 5; - 0x7c832878; - |a, b| a & !b; - } - insn_logic! { - "andc." 3, 4, 5; - 0x7c832879; - |a, b| a & !b; - } - macro_rules! insn_exts { - ( - $mnemonic:literal $dest:literal, $src:literal; - $encoding:literal; - $OutputIntegerMode:ident; - ) => { - retval.push(insn_single( - concat!($mnemonic, " ", stringify!($dest), ", ", stringify!($src)), - $encoding, - None, - LogicalMOp::logical_i( - MOpDestReg::new_sim( - &[MOpRegNum::power_isa_gpr_reg_num($dest)], - if $mnemonic.contains('.') { - &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] - } else { - &[] - }, - ), - [MOpRegNum::power_isa_gpr_reg_imm($src).value], - 0.cast_to_static::>(), - OutputIntegerMode.$OutputIntegerMode(), - Lut4::from_fn(|a, b| a | b), - ), - )); - }; - } - insn_exts! { - "extsb" 3, 4; - 0x7c830774; - SignExt8; - } - insn_exts! { - "extsb." 3, 4; - 0x7c830775; - SignExt8; - } - insn_exts! { - "extsh" 3, 4; - 0x7c830734; - SignExt16; - } - insn_exts! { - "extsh." 3, 4; - 0x7c830735; - SignExt16; - } - insn_exts! { - "extsw" 3, 4; - 0x7c8307b4; - SignExt32; - } - insn_exts! { - "extsw." 3, 4; - 0x7c8307b5; - SignExt32; - } - #[hdl] - fn mcrxrx_imm() -> SimValue { - #[hdl(sim)] - LogicalFlagsMOpImm { - // if the order of flags in PRegFlags changes, this will need to be updated - src0_start: 4usize.cast_to(LogicalFlagsMOpImm.src0_start), - src1_start: 4usize.cast_to(LogicalFlagsMOpImm.src1_start), - src2_start: 4usize.cast_to(LogicalFlagsMOpImm.src2_start), - dest_start: 0usize.cast_to(LogicalFlagsMOpImm.dest_start), - dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count), - } - } - retval.push(insn_single( - "mcrxrx 3", - 0x7d800480, - None, - LogicalFlagsMOp::logical_flags( - MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), - [ - MOpRegNum::power_isa_xer_ca_ca32_reg().value, - MOpRegNum::const_zero().value, - MOpRegNum::power_isa_xer_so_ov_ov32_reg().value, - ], - mcrxrx_imm(), - Lut4::from_fn(|a, b| a | b), - ), - )); - // ensure pnop decodes to zero instructions - retval.push(insn_empty( - // LLVM doesn't support the pnop instruction: - // https://github.com/llvm/llvm-project/issues/176831 - ".long 0x07000000, 0 # pnop", - 0x07000000, - Some(0), - )); - retval.push(insn_single( - "b 0x345678", - 0x48345678, - None, - BranchMOp::branch_i( - MOpDestReg::new_sim(&[], &[]), - MOpRegNum::const_zero().value, - 0x345678.cast_to_static::>(), - true, - false, - false, - ), - )); - retval.push(insn_single( - "ba 0x345678", - 0x4834567a, - None, - BranchMOp::branch_i( - MOpDestReg::new_sim(&[], &[]), - MOpRegNum::const_zero().value, - 0x345678.cast_to_static::>(), - false, - false, - false, - ), - )); - retval.push(insn_single( - "bl 0x345678", - 0x48345679, - None, - BranchMOp::branch_i( - MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), - MOpRegNum::const_zero().value, - 0x345678.cast_to_static::>(), - true, - true, - false, - ), - )); - retval.push(insn_single( - "bla 0x345678", - 0x4834567b, - None, - BranchMOp::branch_i( - MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), - MOpRegNum::const_zero().value, - 0x345678.cast_to_static::>(), - false, - true, - false, - ), - )); - fn insn_dec_ctr_and( - mnemonic: &'static str, - first_input: u32, - second_input: Option, - second_insn: impl ToSimValue, - ) -> TestCase { - insn_double( - mnemonic, - first_input, - second_input, - [ - AddSubMOp::add_sub_i::( - MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []), - [ - MOpRegNum::power_isa_ctr_reg().value, - MOpRegNum::const_zero().value, - ], - (-1).cast_to_static::>(), - OutputIntegerMode.Full64(), - false, - false, - false, - false, - ) - .into_sim_value(), - second_insn.into_sim_value(), - ], - ) - } - macro_rules! insn_branch_conds { - ( - mnemonic = $mnemonic:literal; - mnemonic_l = $mnemonic_l:literal; - asm_last_arg = $asm_last_arg:literal; - imm = $imm:literal; - encoding = $encoding:literal; - src1 = $src1:expr; - pc_relative = $pc_relative:expr; - is_ret = $is_ret:expr; - ) => { - insn_branch_conds! { - mnemonic = $mnemonic; - asm_last_arg = $asm_last_arg; - imm = $imm; - encoding = $encoding; - dest = MOpDestReg::new_sim(&[], &[]); - src1 = $src1; - pc_relative = $pc_relative; - lk = false; - is_ret = $is_ret; - } - insn_branch_conds! { - mnemonic = $mnemonic_l; - asm_last_arg = $asm_last_arg; - imm = $imm; - encoding = $encoding | 1; - dest = MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]); - src1 = $src1; - pc_relative = $pc_relative; - lk = true; - is_ret = $is_ret; - } - }; - ( - mnemonic = $mnemonic:literal; - asm_last_arg = $asm_last_arg:literal; - imm = $imm:literal; - encoding = $encoding:expr; - dest = $dest:expr; - src1 = $src1:expr; - pc_relative = $pc_relative:expr; - lk = $lk:expr; - is_ret = $is_ret:expr; - ) => { - if !$mnemonic.starts_with("bcctr") { - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 0, 0, ", $asm_last_arg), - $encoding, - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.SLt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 0, 1, ", $asm_last_arg), - $encoding | 0x010000, - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.SGt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 0, 2, ", $asm_last_arg), - $encoding | 0x020000, - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.Eq(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 0, 3, ", $asm_last_arg), - $encoding | 0x030000, - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.Overflow(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 0, 9, ", $asm_last_arg), - $encoding | 0x090000, - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(2).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.SGt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 2, 0, ", $asm_last_arg), - $encoding | (2 << 21), - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.SLt(), - false, - $pc_relative, - $lk, - $is_ret, - ), - )); - } - retval.push(insn_single( - concat!($mnemonic, " 4, 0, ", $asm_last_arg), - $encoding | (4 << 21), - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::const_zero().value, - ], - $imm.cast_to_static::>(), - true, - ConditionMode.SLt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - if !$mnemonic.starts_with("bcctr") { - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 8, 0, ", $asm_last_arg), - $encoding | (8 << 21), - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - false, - ConditionMode.SLt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 10, 0, ", $asm_last_arg), - $encoding | (10 << 21), - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - ], - $imm.cast_to_static::>(), - false, - ConditionMode.SLt(), - false, - $pc_relative, - $lk, - $is_ret, - ), - )); - } - retval.push(insn_single( - concat!($mnemonic, " 12, 0, ", $asm_last_arg), - $encoding | (12 << 21), - None, - BranchMOp::branch_cond_ctr( - $dest, - [ - MOpRegNum::power_isa_cr_reg_imm(0).value, - $src1, - MOpRegNum::const_zero().value, - ], - $imm.cast_to_static::>(), - false, - ConditionMode.SLt(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - if !$mnemonic.starts_with("bcctr") { - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 16, 0, ", $asm_last_arg), - $encoding | (16 << 21), - None, - BranchMOp::branch_ctr( - $dest, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - $imm.cast_to_static::>(), - true, - $pc_relative, - $lk, - $is_ret, - ), - )); - retval.push(insn_dec_ctr_and( - concat!($mnemonic, " 18, 0, ", $asm_last_arg), - $encoding | (18 << 21), - None, - BranchMOp::branch_ctr( - $dest, - $src1, - MOpRegNum::power_isa_ctr_reg().value, - $imm.cast_to_static::>(), - false, - $pc_relative, - $lk, - $is_ret, - ), - )); - } - retval.push(insn_single( - concat!($mnemonic, " 20, 0, ", $asm_last_arg), - $encoding | (20 << 21), - None, - BranchMOp::branch_i( - $dest, - $src1, - $imm.cast_to_static::>(), - $pc_relative, - $lk, - $is_ret, - ), - )); - }; - } - insn_branch_conds! { - mnemonic = "bc"; - mnemonic_l = "bcl"; - asm_last_arg = "0x1234"; - imm = 0x1234; - encoding = 0x40001234; - src1 = MOpRegNum::const_zero().value; - pc_relative = true; - is_ret = false; - } - insn_branch_conds! { - mnemonic = "bca"; - mnemonic_l = "bcla"; - asm_last_arg = "0x1234"; - imm = 0x1234; - encoding = 0x40001236; - src1 = MOpRegNum::const_zero().value; - pc_relative = false; - is_ret = false; - } - insn_branch_conds! { - mnemonic = "bclr"; - mnemonic_l = "bclrl"; - asm_last_arg = "0"; - imm = 0; - encoding = 0x4c000020; - src1 = MOpRegNum::power_isa_lr_reg().value; - pc_relative = false; - is_ret = true; - } - insn_branch_conds! { - mnemonic = "bcctr"; - mnemonic_l = "bcctrl"; - asm_last_arg = "0"; - imm = 0; - encoding = 0x4c000420; - src1 = MOpRegNum::power_isa_ctr_reg().value; - pc_relative = false; - is_ret = false; - } - retval.push(insn_dec_ctr_and( - // LLVM doesn't support the bctar[l] instructions: - // https://github.com/llvm/llvm-project/issues/176864 - ".long 0x4e400461 # bctarl 18, 0, 0", - 0x4e400461, - None, - BranchMOp::branch_ctr( - MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), - MOpRegNum::power_isa_tar_reg().value, - MOpRegNum::power_isa_ctr_reg().value, - 0.cast_to_static::>(), - false, - false, - true, - false, - ), - )); - retval -} +mod test_cases; #[test] fn test_test_cases_assembly() -> std::io::Result<()> { @@ -1402,7 +17,7 @@ fn test_test_cases_assembly() -> std::io::Result<()> { .expect("can't find llvm-mc or llvm-mc- in path") .next() .expect("can't find llvm-mc or llvm-mc- in path"); - let test_cases = test_cases(); + let test_cases = test_cases::test_cases(); let mut assembly = String::new(); for TestCase { mnemonic, @@ -1519,7 +134,7 @@ fn test_decode_insn() { second_input, output: _, loc: _, - } in test_cases() + } in test_cases::test_cases() { sim.write(sim.io().first_input, first_input); sim.write( diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases.rs new file mode 100644 index 0000000..7e82bee --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use cpu::{instruction::MOp, util::array_vec::ArrayVec}; +use fayalite::prelude::*; +use std::fmt; + +mod branch; +mod fixed_point_arithmetic; +mod fixed_point_compare; +mod fixed_point_logical; +mod move_to_from_system_register; +mod prefixed_no_operation; + +pub struct TestCase { + pub mnemonic: &'static str, + pub first_input: u32, + pub second_input: Option, + pub output: SimValue>>, + pub loc: &'static std::panic::Location<'static>, +} + +impl fmt::Debug for TestCase { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + mnemonic, + first_input, + second_input, + output, + loc, + } = self; + let mut debug_struct = f.debug_struct("TestCase"); + debug_struct + .field("mnemonic", mnemonic) + .field("first_input", &format_args!("0x{first_input:08x}")); + if let Some(second_input) = second_input { + debug_struct.field("second_input", &format_args!("0x{second_input:08x}")); + } else { + debug_struct.field("second_input", &format_args!("None")); + } + debug_struct + .field("output", &ArrayVec::elements_sim_ref(output)) + .field("loc", &format_args!("{loc}")) + .finish() + } +} + +#[track_caller] +fn insn_empty(mnemonic: &'static str, first_input: u32, second_input: Option) -> TestCase { + let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) + .zero() + .cast_bits_to(MOp); + TestCase { + mnemonic, + first_input, + second_input, + output: ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop), + loc: std::panic::Location::caller(), + } +} + +#[track_caller] +fn insn_single( + mnemonic: &'static str, + first_input: u32, + second_input: Option, + output: impl ToSimValue, +) -> TestCase { + let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) + .zero() + .cast_bits_to(MOp); + let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop); + ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space"); + ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value(); + TestCase { + mnemonic, + first_input, + second_input, + output: single_storage, + loc: std::panic::Location::caller(), + } +} + +#[track_caller] +fn insn_double( + mnemonic: &'static str, + first_input: u32, + second_input: Option, + insns: [impl ToSimValue; 2], +) -> TestCase { + let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) + .zero() + .cast_bits_to(MOp); + let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop); + ArrayVec::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space"); + ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space"); + ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value(); + ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value(); + TestCase { + mnemonic, + first_input, + second_input, + output: single_storage, + loc: std::panic::Location::caller(), + } +} + +pub fn test_cases() -> Vec { + let mut retval = Vec::new(); + fixed_point_arithmetic::test_cases_book_i_3_3_9_fixed_point_arithmetic(&mut retval); + fixed_point_compare::test_cases_book_i_3_3_10_fixed_point_compare(&mut retval); + fixed_point_logical::test_cases_book_i_3_3_13_fixed_point_logical(&mut retval); + move_to_from_system_register::test_cases_book_i_3_3_19_move_to_from_system_register( + &mut retval, + ); + prefixed_no_operation::test_cases_book_i_3_3_20_prefixed_no_operation(&mut retval); + branch::test_cases_book_i_2_4_branch(&mut retval); + retval +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs new file mode 100644 index 0000000..30f193a --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_double, insn_single}; +use cpu::instruction::{ + AddSubMOp, BranchMOp, ConditionMode, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode, +}; +use fayalite::prelude::*; + +/// covers instructions in PowerISA v3.1C Book I 2.4 Branch Instructions +pub fn test_cases_book_i_2_4_branch(retval: &mut Vec) { + retval.push(insn_single( + "b 0x345678", + 0x48345678, + None, + BranchMOp::branch_i( + MOpDestReg::new_sim(&[], &[]), + MOpRegNum::const_zero().value, + 0x345678.cast_to_static::>(), + true, + false, + false, + ), + )); + retval.push(insn_single( + "ba 0x345678", + 0x4834567a, + None, + BranchMOp::branch_i( + MOpDestReg::new_sim(&[], &[]), + MOpRegNum::const_zero().value, + 0x345678.cast_to_static::>(), + false, + false, + false, + ), + )); + retval.push(insn_single( + "bl 0x345678", + 0x48345679, + None, + BranchMOp::branch_i( + MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), + MOpRegNum::const_zero().value, + 0x345678.cast_to_static::>(), + true, + true, + false, + ), + )); + retval.push(insn_single( + "bla 0x345678", + 0x4834567b, + None, + BranchMOp::branch_i( + MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), + MOpRegNum::const_zero().value, + 0x345678.cast_to_static::>(), + false, + true, + false, + ), + )); + fn insn_dec_ctr_and( + mnemonic: &'static str, + first_input: u32, + second_input: Option, + second_insn: impl ToSimValue, + ) -> TestCase { + insn_double( + mnemonic, + first_input, + second_input, + [ + AddSubMOp::add_sub_i::( + MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []), + [ + MOpRegNum::power_isa_ctr_reg().value, + MOpRegNum::const_zero().value, + ], + (-1).cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ) + .into_sim_value(), + second_insn.into_sim_value(), + ], + ) + } + macro_rules! insn_branch_conds { + ( + mnemonic = $mnemonic:literal; + mnemonic_l = $mnemonic_l:literal; + asm_last_arg = $asm_last_arg:literal; + imm = $imm:literal; + encoding = $encoding:literal; + src1 = $src1:expr; + pc_relative = $pc_relative:expr; + is_ret = $is_ret:expr; + ) => { + insn_branch_conds! { + mnemonic = $mnemonic; + asm_last_arg = $asm_last_arg; + imm = $imm; + encoding = $encoding; + dest = MOpDestReg::new_sim(&[], &[]); + src1 = $src1; + pc_relative = $pc_relative; + lk = false; + is_ret = $is_ret; + } + insn_branch_conds! { + mnemonic = $mnemonic_l; + asm_last_arg = $asm_last_arg; + imm = $imm; + encoding = $encoding | 1; + dest = MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]); + src1 = $src1; + pc_relative = $pc_relative; + lk = true; + is_ret = $is_ret; + } + }; + ( + mnemonic = $mnemonic:literal; + asm_last_arg = $asm_last_arg:literal; + imm = $imm:literal; + encoding = $encoding:expr; + dest = $dest:expr; + src1 = $src1:expr; + pc_relative = $pc_relative:expr; + lk = $lk:expr; + is_ret = $is_ret:expr; + ) => { + if !$mnemonic.starts_with("bcctr") { + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 0, 0, ", $asm_last_arg), + $encoding, + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.SLt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 0, 1, ", $asm_last_arg), + $encoding | 0x010000, + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.SGt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 0, 2, ", $asm_last_arg), + $encoding | 0x020000, + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.Eq(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 0, 3, ", $asm_last_arg), + $encoding | 0x030000, + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.Overflow(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 0, 9, ", $asm_last_arg), + $encoding | 0x090000, + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(2).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.SGt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 2, 0, ", $asm_last_arg), + $encoding | (2 << 21), + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.SLt(), + false, + $pc_relative, + $lk, + $is_ret, + ), + )); + } + retval.push(insn_single( + concat!($mnemonic, " 4, 0, ", $asm_last_arg), + $encoding | (4 << 21), + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::const_zero().value, + ], + $imm.cast_to_static::>(), + true, + ConditionMode.SLt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + if !$mnemonic.starts_with("bcctr") { + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 8, 0, ", $asm_last_arg), + $encoding | (8 << 21), + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + false, + ConditionMode.SLt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 10, 0, ", $asm_last_arg), + $encoding | (10 << 21), + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + ], + $imm.cast_to_static::>(), + false, + ConditionMode.SLt(), + false, + $pc_relative, + $lk, + $is_ret, + ), + )); + } + retval.push(insn_single( + concat!($mnemonic, " 12, 0, ", $asm_last_arg), + $encoding | (12 << 21), + None, + BranchMOp::branch_cond_ctr( + $dest, + [ + MOpRegNum::power_isa_cr_reg_imm(0).value, + $src1, + MOpRegNum::const_zero().value, + ], + $imm.cast_to_static::>(), + false, + ConditionMode.SLt(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + if !$mnemonic.starts_with("bcctr") { + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 16, 0, ", $asm_last_arg), + $encoding | (16 << 21), + None, + BranchMOp::branch_ctr( + $dest, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + $imm.cast_to_static::>(), + true, + $pc_relative, + $lk, + $is_ret, + ), + )); + retval.push(insn_dec_ctr_and( + concat!($mnemonic, " 18, 0, ", $asm_last_arg), + $encoding | (18 << 21), + None, + BranchMOp::branch_ctr( + $dest, + $src1, + MOpRegNum::power_isa_ctr_reg().value, + $imm.cast_to_static::>(), + false, + $pc_relative, + $lk, + $is_ret, + ), + )); + } + retval.push(insn_single( + concat!($mnemonic, " 20, 0, ", $asm_last_arg), + $encoding | (20 << 21), + None, + BranchMOp::branch_i( + $dest, + $src1, + $imm.cast_to_static::>(), + $pc_relative, + $lk, + $is_ret, + ), + )); + }; + } + insn_branch_conds! { + mnemonic = "bc"; + mnemonic_l = "bcl"; + asm_last_arg = "0x1234"; + imm = 0x1234; + encoding = 0x40001234; + src1 = MOpRegNum::const_zero().value; + pc_relative = true; + is_ret = false; + } + insn_branch_conds! { + mnemonic = "bca"; + mnemonic_l = "bcla"; + asm_last_arg = "0x1234"; + imm = 0x1234; + encoding = 0x40001236; + src1 = MOpRegNum::const_zero().value; + pc_relative = false; + is_ret = false; + } + insn_branch_conds! { + mnemonic = "bclr"; + mnemonic_l = "bclrl"; + asm_last_arg = "0"; + imm = 0; + encoding = 0x4c000020; + src1 = MOpRegNum::power_isa_lr_reg().value; + pc_relative = false; + is_ret = true; + } + insn_branch_conds! { + mnemonic = "bcctr"; + mnemonic_l = "bcctrl"; + asm_last_arg = "0"; + imm = 0; + encoding = 0x4c000420; + src1 = MOpRegNum::power_isa_ctr_reg().value; + pc_relative = false; + is_ret = false; + } + retval.push(insn_dec_ctr_and( + // LLVM doesn't support the bctar[l] instructions: + // https://github.com/llvm/llvm-project/issues/176864 + ".long 0x4e400461 # bctarl 18, 0, 0", + 0x4e400461, + None, + BranchMOp::branch_ctr( + MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]), + MOpRegNum::power_isa_tar_reg().value, + MOpRegNum::power_isa_ctr_reg().value, + 0.cast_to_static::>(), + false, + false, + true, + false, + ), + )); +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_arithmetic.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_arithmetic.rs new file mode 100644 index 0000000..a386cff --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_arithmetic.rs @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_single}; +use cpu::instruction::{AddSubMOp, MOpDestReg, MOpRegNum, OutputIntegerMode}; +use fayalite::prelude::*; + +/// covers instructions in PowerISA v3.1C Book I 3.3.9 Fixed-Point Arithmetic Instructions +pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec) { + retval.push(insn_single( + "addi 3, 4, 0x1234", + 0x38641234, + None, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + ], + 0x1234.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "paddi 3, 4, 0x123456789, 0", + 0x06012345, + Some(0x38646789), + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + ], + 0x123456789i64.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "paddi 3, 0, 0x123456789, 1", + 0x06112345, + Some(0x38606789), + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [MOpRegNum::const_zero().value, MOpRegNum::const_zero().value], + 0x123456789i64.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + true, + ), + )); + retval.push(insn_single( + "addis 3, 4, 0x1234", + 0x3C641234, + None, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + ], + 0x12340000.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "addpcis 3, 0x1234", + 0x4c7a1204, + None, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [MOpRegNum::const_zero().value; _], + 0x12340004.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + true, + ), + )); + retval.push(insn_single( + "add. 3, 4, 5", + 0x7c642a15, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num(3)], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "addic. 3, 4, 0x1234", + 0x34641234, + None, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + ], + 0x1234.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "subf. 3, 4, 5", + 0x7c642851, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num(3)], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + false, + true, + false, + ), + )); + retval.push(insn_single( + "subfic 3, 4, 0x1234", + 0x20641234, + None, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + ], + 0x1234.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + false, + true, + false, + ), + )); + retval.push(insn_single( + "addc. 3, 4, 5", + 0x7c642815, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + )); + retval.push(insn_single( + "subfc. 3, 4, 5", + 0x7c642811, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + false, + true, + false, + ), + )); + retval.push(insn_single( + "adde. 3, 4, 5", + 0x7c642915, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + true, + false, + false, + ), + )); + retval.push(insn_single( + "subfe. 3, 4, 5", + 0x7c642911, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + true, + false, + false, + ), + )); + retval.push(insn_single( + "addme. 3, 4", + 0x7c6401d5, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::const_zero().value, + ], + (-1i8).cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + true, + false, + false, + ), + )); + retval.push(insn_single( + "subfme. 3, 4", + 0x7c6401d1, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::const_zero().value, + ], + (-1i8).cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + true, + false, + false, + ), + )); + retval.push(insn_single( + "addze. 3, 4", + 0x7c640195, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + false, + true, + false, + false, + ), + )); + retval.push(insn_single( + "subfze. 3, 4", + 0x7c640191, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[ + MOpRegNum::power_isa_gpr_reg_num(3), + MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM, + ], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + true, + false, + false, + ), + )); + retval.push(insn_single( + "neg. 3, 4", + 0x7c6400d1, + None, + AddSubMOp::add_sub( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num(3)], + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM], + ), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::const_zero().value, + MOpRegNum::const_zero().value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + true, + false, + true, + false, + ), + )); +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_compare.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_compare.rs new file mode 100644 index 0000000..37d4c00 --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_compare.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_single}; +use cpu::instruction::{CompareMOp, CompareMode, MOpDestReg, MOpRegNum, OutputIntegerMode}; +use fayalite::prelude::*; + +/// covers instructions in PowerISA v3.1C Book I 3.3.10 Fixed-Point Compare Instructions +pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec) { + retval.push(insn_single( + "cmpi 3, 0, 4, 0x1234", + 0x2d841234, + None, + CompareMOp::compare_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [MOpRegNum::power_isa_gpr_reg_imm(4).value], + 0x1234.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.S32(), + ), + )); + retval.push(insn_single( + "cmpi 3, 1, 4, -0x7655", + 0x2da489ab, + None, + CompareMOp::compare_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [MOpRegNum::power_isa_gpr_reg_imm(4).value], + (0x89abu16 as i16).cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.S64(), + ), + )); + retval.push(insn_single( + "cmp 3, 0, 4, 5", + 0x7d842800, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.S32(), + ), + )); + retval.push(insn_single( + "cmp 3, 1, 4, 5", + 0x7da42800, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.S64(), + ), + )); + retval.push(insn_single( + "cmpli 3, 0, 4, 0x1234", + 0x29841234, + None, + CompareMOp::compare_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [MOpRegNum::power_isa_gpr_reg_imm(4).value], + 0x1234.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.U32(), + ), + )); + retval.push(insn_single( + "cmpli 3, 1, 4, 0x89ab", + 0x29a489ab, + None, + CompareMOp::compare_i( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [MOpRegNum::power_isa_gpr_reg_imm(4).value], + 0x89ab.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.U64(), + ), + )); + retval.push(insn_single( + "cmpl 3, 0, 4, 5", + 0x7d842840, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.U32(), + ), + )); + retval.push(insn_single( + "cmpl 3, 1, 4, 5", + 0x7da42840, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.U64(), + ), + )); + retval.push(insn_single( + "cmprb 3, 0, 4, 5", + 0x7d842980, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.CmpRBOne(), + ), + )); + retval.push(insn_single( + "cmprb 3, 1, 4, 5", + 0x7da42980, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.CmpRBTwo(), + ), + )); + retval.push(insn_single( + "cmpeqb 3, 4, 5", + 0x7d8429c0, + None, + CompareMOp::compare( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_gpr_reg_imm(4).value, + MOpRegNum::power_isa_gpr_reg_imm(5).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + CompareMode.CmpEqB(), + ), + )); +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_logical.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_logical.rs new file mode 100644 index 0000000..f26b094 --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/fixed_point_logical.rs @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_empty, insn_single}; +use cpu::instruction::{LogicalMOp, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, OutputIntegerMode}; +use fayalite::prelude::*; + +/// covers instructions in PowerISA v3.1C Book I 3.3.13 Fixed-Point Logical Instructions +pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec) { + macro_rules! insn_logic_i { + ( + $mnemonic:literal $dest:literal, $src:literal, $imm:literal; + $encoding:literal; + |$a:ident, $b:ident| $lut_fn:expr; + ) => { + retval.push(insn_single( + concat!( + $mnemonic, + " ", + stringify!($dest), + ", ", + stringify!($src), + ", ", + stringify!($imm) + ), + $encoding, + None, + LogicalMOp::logical_i( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num($dest)], + if $mnemonic.contains('.') { + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] + } else { + &[] + }, + ), + [MOpRegNum::power_isa_gpr_reg_imm($src).value], + (($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 }) + .cast_to_static::>(), + OutputIntegerMode.Full64(), + Lut4::from_fn(|$a, $b| $lut_fn), + ), + )); + }; + } + insn_logic_i! { + "andi." 3, 4, 0x89ab; + 0x708389ab; + |a, b| a & b; + } + insn_logic_i! { + "andis." 3, 4, 0x89ab; + 0x748389ab; + |a, b| a & b; + } + insn_logic_i! { + "ori" 3, 4, 0x89ab; + 0x608389ab; + |a, b| a | b; + } + // ensure nop decodes to zero instructions + retval.push(insn_empty("ori 0, 0, 0", 0x60000000, None)); + insn_logic_i! { + "oris" 3, 4, 0x89ab; + 0x648389ab; + |a, b| a | b; + } + insn_logic_i! { + "xori" 3, 4, 0x89ab; + 0x688389ab; + |a, b| a ^ b; + } + insn_logic_i! { + "xori" 0, 0, 0; // ensure xnop actually decodes to a normal ALU instruction + 0x68000000; + |a, b| a ^ b; + } + insn_logic_i! { + "xoris" 3, 4, 0x89ab; + 0x6c8389ab; + |a, b| a ^ b; + } + macro_rules! insn_logic { + ( + $mnemonic:literal $dest:literal, $src0:literal, $src1:literal; + $encoding:literal; + |$a:ident, $b:ident| $lut_fn:expr; + ) => { + retval.push(insn_single( + concat!( + $mnemonic, + " ", + stringify!($dest), + ", ", + stringify!($src0), + ", ", + stringify!($src1) + ), + $encoding, + None, + LogicalMOp::logical( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num($dest)], + if $mnemonic.contains('.') { + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] + } else { + &[] + }, + ), + [ + MOpRegNum::power_isa_gpr_reg_imm($src0).value, + MOpRegNum::power_isa_gpr_reg_imm($src1).value, + ], + 0.cast_to_static::>(), + OutputIntegerMode.Full64(), + Lut4::from_fn(|$a, $b| $lut_fn), + ), + )); + }; + } + insn_logic! { + "and" 3, 4, 5; + 0x7c832838; + |a, b| a & b; + } + insn_logic! { + "and." 3, 4, 5; + 0x7c832839; + |a, b| a & b; + } + insn_logic! { + "xor" 3, 4, 5; + 0x7c832a78; + |a, b| a ^ b; + } + insn_logic! { + "xor." 3, 4, 5; + 0x7c832a79; + |a, b| a ^ b; + } + insn_logic! { + "nand" 3, 4, 5; + 0x7c832bb8; + |a, b| !(a & b); + } + insn_logic! { + "nand." 3, 4, 5; + 0x7c832bb9; + |a, b| !(a & b); + } + insn_logic! { + "or" 3, 4, 5; + 0x7c832b78; + |a, b| a | b; + } + retval.push(insn_single( + "or 3, 4, 4", // mr 3, 4 + 0x7c832378, + None, + MoveRegMOp::move_reg( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]), + [MOpRegNum::power_isa_gpr_reg_imm(4).value], + 0.cast_to_static::>(), + ), + )); + insn_logic! { + "or." 3, 4, 5; + 0x7c832b79; + |a, b| a | b; + } + insn_logic! { + "or." 3, 4, 4; // mr. 3, 4 + 0x7c832379; + |a, b| a | b; + } + insn_logic! { + "orc" 3, 4, 5; + 0x7c832b38; + |a, b| a | !b; + } + insn_logic! { + "orc." 3, 4, 5; + 0x7c832b39; + |a, b| a | !b; + } + insn_logic! { + "nor" 3, 4, 5; + 0x7c8328f8; + |a, b| !(a | b); + } + insn_logic! { + "nor." 3, 4, 5; + 0x7c8328f9; + |a, b| !(a | b); + } + insn_logic! { + "eqv" 3, 4, 5; + 0x7c832a38; + |a, b| a == b; + } + insn_logic! { + "eqv." 3, 4, 5; + 0x7c832a39; + |a, b| a == b; + } + insn_logic! { + "andc" 3, 4, 5; + 0x7c832878; + |a, b| a & !b; + } + insn_logic! { + "andc." 3, 4, 5; + 0x7c832879; + |a, b| a & !b; + } + macro_rules! insn_exts { + ( + $mnemonic:literal $dest:literal, $src:literal; + $encoding:literal; + $OutputIntegerMode:ident; + ) => { + retval.push(insn_single( + concat!($mnemonic, " ", stringify!($dest), ", ", stringify!($src)), + $encoding, + None, + LogicalMOp::logical_i( + MOpDestReg::new_sim( + &[MOpRegNum::power_isa_gpr_reg_num($dest)], + if $mnemonic.contains('.') { + &[MOpRegNum::POWER_ISA_CR_0_REG_NUM] + } else { + &[] + }, + ), + [MOpRegNum::power_isa_gpr_reg_imm($src).value], + 0.cast_to_static::>(), + OutputIntegerMode.$OutputIntegerMode(), + Lut4::from_fn(|a, b| a | b), + ), + )); + }; + } + insn_exts! { + "extsb" 3, 4; + 0x7c830774; + SignExt8; + } + insn_exts! { + "extsb." 3, 4; + 0x7c830775; + SignExt8; + } + insn_exts! { + "extsh" 3, 4; + 0x7c830734; + SignExt16; + } + insn_exts! { + "extsh." 3, 4; + 0x7c830735; + SignExt16; + } + insn_exts! { + "extsw" 3, 4; + 0x7c8307b4; + SignExt32; + } + insn_exts! { + "extsw." 3, 4; + 0x7c8307b5; + SignExt32; + } +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/move_to_from_system_register.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/move_to_from_system_register.rs new file mode 100644 index 0000000..8573881 --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/move_to_from_system_register.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_single}; +use cpu::instruction::{LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum}; +use fayalite::prelude::*; + +/// covers instructions in PowerISA v3.1C Book I 3.3.19 Move To/From System Register Instructions +pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec) { + #[hdl] + fn mcrxrx_imm() -> SimValue { + #[hdl(sim)] + LogicalFlagsMOpImm { + // if the order of flags in PRegFlags changes, this will need to be updated + src0_start: 4usize.cast_to(LogicalFlagsMOpImm.src0_start), + src1_start: 4usize.cast_to(LogicalFlagsMOpImm.src1_start), + src2_start: 4usize.cast_to(LogicalFlagsMOpImm.src2_start), + dest_start: 0usize.cast_to(LogicalFlagsMOpImm.dest_start), + dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count), + } + } + retval.push(insn_single( + "mcrxrx 3", + 0x7d800480, + None, + LogicalFlagsMOp::logical_flags( + MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]), + [ + MOpRegNum::power_isa_xer_ca_ca32_reg().value, + MOpRegNum::const_zero().value, + MOpRegNum::power_isa_xer_so_ov_ov32_reg().value, + ], + mcrxrx_imm(), + Lut4::from_fn(|a, b| a | b), + ), + )); +} diff --git a/crates/cpu/tests/simple_power_isa_decoder/test_cases/prefixed_no_operation.rs b/crates/cpu/tests/simple_power_isa_decoder/test_cases/prefixed_no_operation.rs new file mode 100644 index 0000000..b03f023 --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder/test_cases/prefixed_no_operation.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::test_cases::{TestCase, insn_empty}; + +/// covers instructions in PowerISA v3.1C Book I 3.3.20 Prefixed No-Operation Instruction +pub fn test_cases_book_i_3_3_20_prefixed_no_operation(retval: &mut Vec) { + // ensure pnop decodes to zero instructions + retval.push(insn_empty( + // LLVM doesn't support the pnop instruction: + // https://github.com/llvm/llvm-project/issues/176831 + ".long 0x07000000, 0 # pnop", + 0x07000000, + Some(0), + )); +}