From d1306e6acc6e2a85e78b6ce16f2b1f817283269b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 27 May 2020 23:13:50 -0700 Subject: [PATCH] working on adding instruction models --- Cargo.toml | 4 + src/instr_models.rs | 233 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 101 +++++++++++++------ 3 files changed, 310 insertions(+), 28 deletions(-) create mode 100644 src/instr_models.rs diff --git a/Cargo.toml b/Cargo.toml index 8992fa9..14461b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,10 @@ authors = ["Jacob Lifshay "] edition = "2018" license = "LGPL-2.1-or-later" +[features] +native_instrs = [] +default = ["native_instrs"] + [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" \ No newline at end of file diff --git a/src/instr_models.rs b/src/instr_models.rs new file mode 100644 index 0000000..a609bcb --- /dev/null +++ b/src/instr_models.rs @@ -0,0 +1,233 @@ +use crate::{OverflowFlags, TestDivInput, TestDivResult}; + +pub fn divdeo(inputs: TestDivInput) -> TestDivResult { + let dividend = i128::from(inputs.dividend as i64) << 64; + let divisor = i128::from(inputs.divisor as i64); + let overflow; + let result; + if divisor == 0 || (divisor == -1 && dividend == i128::min_value()) { + result = 0; + overflow = true; + } else { + let result128 = dividend / divisor; + result = result128 as u64; + overflow = result128 as i64 as i128 != result128; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divdeuo(inputs: TestDivInput) -> TestDivResult { + let dividend = u128::from(inputs.dividend) << 64; + let divisor = u128::from(inputs.divisor); + let overflow; + let result; + if divisor == 0 { + result = 0; + overflow = true; + } else { + let resultu128 = dividend / divisor; + result = resultu128 as u64; + overflow = resultu128 > u128::from(u64::max_value()); + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divdo(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as i64; + let divisor = inputs.divisor as i64; + let overflow; + let result; + if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) { + result = 0; + overflow = true; + } else { + result = (dividend / divisor) as u64; + overflow = false; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divduo(inputs: TestDivInput) -> TestDivResult { + let dividend: u64 = inputs.dividend; + let divisor: u64 = inputs.divisor; + let overflow; + let result; + if divisor == 0 { + result = 0; + overflow = true; + } else { + result = dividend / divisor; + overflow = false; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divweo(inputs: TestDivInput) -> TestDivResult { + let dividend = i64::from(inputs.dividend as i32) << 32; + let divisor = i64::from(inputs.divisor as i32); + let overflow; + let result; + if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) { + result = 0; + overflow = true; + } else { + let result64 = dividend / divisor; + result = result64 as u32 as u64; + overflow = result64 as i32 as i64 != result64; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divweuo(inputs: TestDivInput) -> TestDivResult { + let dividend = u64::from(inputs.dividend) << 32; + let divisor = u64::from(inputs.divisor); + let overflow; + let result; + if divisor == 0 { + result = 0; + overflow = true; + } else { + let resultu64 = dividend / divisor; + result = resultu64 as u32 as u64; + overflow = resultu64 > u64::from(u32::max_value()); + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divwo(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as i32; + let divisor = inputs.divisor as i32; + let overflow; + let result; + if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) { + result = 0; + overflow = true; + } else { + result = (dividend / divisor) as u32 as u64; + overflow = false; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn divwuo(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as u32; + let divisor = inputs.divisor as u32; + let overflow; + let result; + if divisor == 0 { + result = 0; + overflow = true; + } else { + result = (dividend / divisor) as u64; + overflow = false; + } + TestDivResult { + result, + overflow: Some(OverflowFlags { + overflow, + overflow32: overflow, + }), + } +} + +pub fn modsd(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as i64; + let divisor = inputs.divisor as i64; + let result; + if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) { + result = 0; + } else { + result = (dividend % divisor) as u64; + } + TestDivResult { + result, + overflow: None, + } +} + +pub fn modud(inputs: TestDivInput) -> TestDivResult { + let dividend: u64 = inputs.dividend; + let divisor: u64 = inputs.divisor; + let result; + if divisor == 0 { + result = 0; + } else { + result = dividend % divisor; + } + TestDivResult { + result, + overflow: None, + } +} + +pub fn modsw(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as i32; + let divisor = inputs.divisor as i32; + let result; + if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) { + result = 0; + } else { + result = (dividend % divisor) as u32 as u64; + } + TestDivResult { + result, + overflow: None, + } +} + +pub fn moduw(inputs: TestDivInput) -> TestDivResult { + let dividend = inputs.dividend as u32; + let divisor = inputs.divisor as u32; + let result; + if divisor == 0 { + result = 0; + } else { + result = (dividend % divisor) as u64; + } + TestDivResult { + result, + overflow: None, + } +} diff --git a/src/main.rs b/src/main.rs index 657466b..aa9bda7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // See Notices.txt for copyright information -#![feature(llvm_asm)] +#![cfg_attr(feature = "native_instrs", feature(llvm_asm))] +mod instr_models; mod serde_hex; use serde::{Deserialize, Serialize}; -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct OverflowFlags { pub overflow: bool, pub overflow32: bool, @@ -22,7 +23,7 @@ impl OverflowFlags { } } -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct TestDivResult { #[serde(with = "serde_hex::SerdeHex")] pub result: u64, @@ -40,13 +41,27 @@ pub struct TestDivInput { pub result_prev: u64, } +fn is_false(v: &bool) -> bool { + !v +} + #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct TestDivCase { pub instr: TestDivInstr, #[serde(flatten)] pub inputs: TestDivInput, - #[serde(flatten)] - pub outputs: TestDivResult, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub native_outputs: Option, + pub model_outputs: TestDivResult, + #[serde(default, skip_serializing_if = "is_false")] + pub model_mismatch: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct WholeTest { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub test_div_cases: Vec, + pub any_model_mismatch: bool, } macro_rules! make_div_functions { @@ -73,13 +88,24 @@ macro_rules! make_div_functions { } impl TestDivInstr { - pub fn get_fn(self) -> fn(TestDivInput) -> TestDivResult { + #[cfg(feature = "native_instrs")] + pub fn get_native_fn(self) -> fn(TestDivInput) -> TestDivResult { match self { $( - Self::$div_enum => TestDivInput::$div_fn, + Self::$div_enum => native_instrs::$div_fn, )+ $( - Self::$rem_enum => TestDivInput::$rem_fn, + Self::$rem_enum => native_instrs::$rem_fn, + )+ + } + } + pub fn get_model_fn(self) -> fn(TestDivInput) -> TestDivResult { + match self { + $( + Self::$div_enum => instr_models::$div_fn, + )+ + $( + Self::$rem_enum => instr_models::$rem_fn, )+ } } @@ -103,14 +129,17 @@ macro_rules! make_div_functions { ]; } - impl TestDivInput { + #[cfg(feature = "native_instrs")] + mod native_instrs { + use super::*; + $( - pub fn $div_fn(self) -> TestDivResult { - let Self { + pub fn $div_fn(inputs: TestDivInput) -> TestDivResult { + let TestDivInput { dividend, divisor, result_prev, - } = self; + } = inputs; let result: u64; let xer: u64; unsafe { @@ -131,12 +160,12 @@ macro_rules! make_div_functions { } )+ $( - pub fn $rem_fn(self) -> TestDivResult { - let Self { + pub fn $rem_fn(inputs: TestDivInput) -> TestDivResult { + let TestDivInput { dividend, divisor, result_prev, - } = self; + } = inputs; let result: u64; unsafe { llvm_asm!( @@ -160,14 +189,14 @@ macro_rules! make_div_functions { make_div_functions! { #[div] { - DivDE = divde("divdeo"), - DivDEU = divdeu("divdeuo"), - DivD = divd("divdo"), - DivDU = divdu("divduo"), - DivWE = divwe("divweo"), - DivWEU = divweu("divweuo"), - DivW = divw("divwo"), - DivWU = divwu("divwuo"), + DivDEO = divdeo("divdeo"), + DivDEUO = divdeuo("divdeuo"), + DivDO = divdo("divdo"), + DivDUO = divduo("divduo"), + DivWEO = divweo("divweo"), + DivWEUO = divweuo("divweuo"), + DivWO = divwo("divwo"), + DivWUO = divwuo("divwuo"), } #[rem] { @@ -190,7 +219,8 @@ const TEST_VALUES: &[u64] = &[ ]; fn main() { - let mut cases = Vec::new(); + let mut test_div_cases = Vec::new(); + let mut any_model_mismatch = false; for &instr in TestDivInstr::VALUES { for ÷nd in TEST_VALUES { for &divisor in TEST_VALUES { @@ -199,15 +229,30 @@ fn main() { divisor, result_prev: 0xFECD_BA98_7654_3210, }; - let outputs = instr.get_fn()(inputs); - cases.push(TestDivCase { + let model_outputs = instr.get_model_fn()(inputs); + #[cfg(feature = "native_instrs")] + let native_outputs = Some(instr.get_native_fn()(inputs)); + #[cfg(not(feature = "native_instrs"))] + let native_outputs = None; + let model_mismatch = match native_outputs { + Some(native_outputs) if native_outputs != model_outputs => true, + _ => false, + }; + any_model_mismatch |= model_mismatch; + test_div_cases.push(TestDivCase { instr, inputs, - outputs, + native_outputs, + model_outputs, + model_mismatch, }); } } } - serde_json::to_writer_pretty(std::io::stdout().lock(), &cases).unwrap(); + let whole_test = WholeTest { + test_div_cases, + any_model_mismatch, + }; + serde_json::to_writer_pretty(std::io::stdout().lock(), &whole_test).unwrap(); println!(); } -- 2.30.2