Path: blob/main/cranelift/isle/veri/veri_engine/tests/utils/mod.rs
1693 views
use cranelift_codegen_meta::{1generate_isle,2isle::{get_isle_compilations, shared_isle_lower_paths},3};4use cranelift_isle::compile::create_envs;5use std::env;6use std::path::PathBuf;7use strum::IntoEnumIterator;8use strum_macros::EnumIter;9use veri_engine_lib::annotations::parse_annotations;10use veri_engine_lib::type_inference::type_rules_with_term_and_types;11use veri_engine_lib::verify::verify_rules_for_term;12use veri_engine_lib::Config;13use veri_ir::{ConcreteTest, Counterexample, TermSignature, VerificationResult};1415#[derive(Debug, EnumIter, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]16#[repr(usize)]17pub enum Bitwidth {18I8 = 8,19I16 = 16,20I32 = 32,21I64 = 64,22}2324pub enum TestResult {25Simple(Vec<(Bitwidth, VerificationResult)>),26Expect(fn(&TermSignature) -> VerificationResult),27}2829type TestResultBuilder = dyn Fn(Bitwidth) -> (Bitwidth, VerificationResult);3031use std::sync::Once;3233static INIT: Once = Once::new();3435pub fn get_isle_files(name: &str) -> Vec<std::path::PathBuf> {36let cur_dir = env::current_dir().expect("Can't access current working directory");37let gen_dir = cur_dir.join("test_output");38INIT.call_once(|| {39// Logger40env_logger::init();41// Test directory42if !gen_dir.is_dir() {43std::fs::create_dir(gen_dir.as_path()).unwrap();44}45// Generate ISLE files.46generate_isle(gen_dir.as_path()).expect("Can't generate ISLE");47});4849let codegen_crate_dir = cur_dir.join("../../../codegen");50let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle");5152match name {53"shared_lower" => {54let mut shared = shared_isle_lower_paths(codegen_crate_dir.as_path());55shared.push(gen_dir.join("clif_lower.isle"));56shared57}58_ => {59// Lookup ISLE shared .60let compilations =61get_isle_compilations(codegen_crate_dir.as_path(), gen_dir.as_path());6263// Return inputs from the matching compilation, if any.64let mut inputs = compilations.lookup(name).unwrap().inputs();65inputs.push(inst_specs_isle);66inputs67}68}69}7071// Some examples of functions we might need72#[allow(dead_code)]73pub fn just_8_result() -> TestResult {74TestResult::Simple(vec![(Bitwidth::I8, VerificationResult::Success)])75}7677#[allow(dead_code)]78pub fn just_16_result() -> TestResult {79TestResult::Simple(vec![(Bitwidth::I16, VerificationResult::Success)])80}8182#[allow(dead_code)]83pub fn just_32_result() -> TestResult {84TestResult::Simple(vec![(Bitwidth::I32, VerificationResult::Success)])85}8687#[allow(dead_code)]88pub fn just_64_result() -> TestResult {89TestResult::Simple(vec![(Bitwidth::I64, VerificationResult::Success)])90}9192/// All bitwidths verify93#[allow(dead_code)]94pub fn all_success_result() -> Vec<(Bitwidth, VerificationResult)> {95custom_result(&|w| (w, VerificationResult::Success))96}9798/// All bitwidths fail99#[allow(dead_code)]100pub fn all_failure_result() -> Vec<(Bitwidth, VerificationResult)> {101custom_result(&|w| (w, VerificationResult::Failure(Counterexample {})))102}103104/// Specify a custom set expected result (helpful if you want to test all the bitwidths and expect105/// a range of different success, failure, and inapplicable outcomes)106pub fn custom_result(f: &TestResultBuilder) -> Vec<(Bitwidth, VerificationResult)> {107Bitwidth::iter().map(f).collect()108}109110fn test_rules_with_term(inputs: Vec<PathBuf>, tr: TestResult, config: Config) {111let (typeenv, termenv, defs) = create_envs(inputs).unwrap();112let annotation_env = parse_annotations(&defs, &termenv, &typeenv);113114let term_signatures = annotation_env115.get_term_signatures_by_name(&termenv, &typeenv)116.get(config.term.as_str())117.unwrap_or_else(|| panic!("Missing term type instantiation for {}", config.term))118.clone();119let instantiations = match tr {120TestResult::Simple(s) => {121let mut res = vec![];122for (width, result) in s {123let ty = match width {124Bitwidth::I8 => veri_ir::Type::BitVector(Some(8)),125Bitwidth::I16 => veri_ir::Type::BitVector(Some(16)),126Bitwidth::I32 => veri_ir::Type::BitVector(Some(32)),127Bitwidth::I64 => veri_ir::Type::BitVector(Some(64)),128};129// Find the type instantiations with this as the canonical type130let all_instantiations: Vec<&TermSignature> = term_signatures131.iter()132.filter(|sig| sig.canonical_type.unwrap() == ty)133.collect();134if all_instantiations.is_empty() {135panic!("Missing type instantiation for width {width:?}");136}137for i in all_instantiations {138res.push((i.clone(), result.clone()));139}140}141res142}143TestResult::Expect(expect) => term_signatures144.iter()145.map(|sig| (sig.clone(), expect(sig)))146.collect(),147};148149for (type_instantiation, expected_result) in instantiations {150log::debug!("Expected result: {expected_result:?}");151let type_sols = type_rules_with_term_and_types(152&termenv,153&typeenv,154&annotation_env,155&config,156&type_instantiation,157&None,158);159let result = verify_rules_for_term(160&termenv,161&typeenv,162&type_sols,163type_instantiation,164&None,165&config,166);167assert_eq!(result, expected_result);168}169}170171pub fn test_from_file_with_lhs_termname_simple(172file: &str,173termname: String,174tr: Vec<(Bitwidth, VerificationResult)>,175) {176test_from_file_with_lhs_termname(file, termname, TestResult::Simple(tr))177}178179pub fn test_from_file_with_lhs_termname(file: &str, termname: String, tr: TestResult) {180println!("Verifying {termname} rules in file: {file}");181let mut inputs = get_isle_files("shared_lower");182inputs.push(PathBuf::from(file));183let config = Config {184term: termname,185distinct_check: true,186custom_verification_condition: None,187custom_assumptions: None,188names: None,189};190test_rules_with_term(inputs, tr, config);191}192193pub fn test_aarch64_rule_with_lhs_termname_simple(194rulename: &str,195termname: &str,196tr: Vec<(Bitwidth, VerificationResult)>,197) {198test_aarch64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr))199}200201pub fn test_aarch64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) {202println!("Verifying rule `{rulename}` with termname {termname} ");203let inputs = get_isle_files("aarch64");204let config = Config {205term: termname.to_string(),206distinct_check: true,207custom_verification_condition: None,208custom_assumptions: None,209names: Some(vec![rulename.to_string()]),210};211test_rules_with_term(inputs, tr, config);212}213214pub fn test_x64_rule_with_lhs_termname_simple(215rulename: &str,216termname: &str,217tr: Vec<(Bitwidth, VerificationResult)>,218) {219test_x64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr))220}221222pub fn test_x64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) {223println!("Verifying rule `{rulename}` with termname {termname} ");224let inputs = get_isle_files("x64");225let config = Config {226term: termname.to_string(),227distinct_check: true,228custom_verification_condition: None,229custom_assumptions: None,230names: Some(vec![rulename.to_string()]),231};232test_rules_with_term(inputs, tr, config);233}234235pub fn test_from_file_with_config_simple(236file: &str,237config: Config,238tr: Vec<(Bitwidth, VerificationResult)>,239) {240test_from_file_with_config(file, config, TestResult::Simple(tr))241}242pub fn test_from_file_with_config(file: &str, config: Config, tr: TestResult) {243println!("Verifying {} rules in file: {}", config.term, file);244let mut inputs = get_isle_files("shared_lower");245inputs.push(PathBuf::from(file));246test_rules_with_term(inputs, tr, config);247}248249pub fn test_aarch64_with_config_simple(config: Config, tr: Vec<(Bitwidth, VerificationResult)>) {250test_aarch64_with_config(config, TestResult::Simple(tr))251}252253pub fn test_aarch64_with_config(config: Config, tr: TestResult) {254println!(255"Verifying rules {:?} with termname {}",256config.names, config.term257);258let inputs = get_isle_files("aarch64");259test_rules_with_term(inputs, tr, config);260}261262pub fn test_concrete_aarch64_rule_with_lhs_termname(263rulename: &str,264termname: &str,265concrete: ConcreteTest,266) {267println!("Verifying concrete input rule `{rulename}` with termname {termname} ");268let inputs = get_isle_files("aarch64");269let (typeenv, termenv, defs) = create_envs(inputs).unwrap();270let annotation_env = parse_annotations(&defs, &termenv, &typeenv);271272let config = Config {273term: termname.to_string(),274distinct_check: false,275custom_verification_condition: None,276custom_assumptions: None,277names: Some(vec![rulename.to_string()]),278};279280// Get the types/widths for this particular term281let args = concrete.args.iter().map(|i| i.ty).collect();282let ret = concrete.output.ty;283let t = TermSignature {284args,285ret,286canonical_type: None,287};288289let type_sols = type_rules_with_term_and_types(290&termenv,291&typeenv,292&annotation_env,293&config,294&t,295&Some(concrete.clone()),296);297let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config);298assert_eq!(result, VerificationResult::Success);299}300301pub fn test_concrete_input_from_file_with_lhs_termname(302file: &str,303termname: String,304concrete: ConcreteTest,305) {306println!("Verifying concrete input {termname} rule in file: {file}");307let mut inputs = get_isle_files("shared_lower");308inputs.push(PathBuf::from(file));309310let (typeenv, termenv, defs) = create_envs(inputs).unwrap();311let annotation_env = parse_annotations(&defs, &termenv, &typeenv);312313let config = Config {314term: termname.clone(),315distinct_check: false,316custom_verification_condition: None,317custom_assumptions: None,318names: None,319};320321// Get the types/widths for this particular term322let args = concrete.args.iter().map(|i| i.ty).collect();323let ret = concrete.output.ty;324let t = TermSignature {325args,326ret,327canonical_type: None,328};329330let type_sols = type_rules_with_term_and_types(331&termenv,332&typeenv,333&annotation_env,334&config,335&t,336&Some(concrete.clone()),337);338let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config);339assert_eq!(result, VerificationResult::Success);340}341342343