1 use cranelift_codegen_meta::{
2 generate_isle,
3 isle::{get_isle_compilations, shared_isle_lower_paths},
4 };
5 use cranelift_isle::compile::create_envs;
6 use std::env;
7 use std::path::PathBuf;
8 use strum::IntoEnumIterator;
9 use strum_macros::EnumIter;
10 use veri_engine_lib::annotations::parse_annotations;
11 use veri_engine_lib::type_inference::type_rules_with_term_and_types;
12 use veri_engine_lib::verify::verify_rules_for_term;
13 use veri_engine_lib::Config;
14 use veri_ir::{ConcreteTest, Counterexample, TermSignature, VerificationResult};
15
16 #[derive(Debug, EnumIter, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
17 #[repr(usize)]
18 pub enum Bitwidth {
19 I8 = 8,
20 I16 = 16,
21 I32 = 32,
22 I64 = 64,
23 }
24
25 pub enum TestResult {
26 Simple(Vec<(Bitwidth, VerificationResult)>),
27 Expect(fn(&TermSignature) -> VerificationResult),
28 }
29
30 type TestResultBuilder = dyn Fn(Bitwidth) -> (Bitwidth, VerificationResult);
31
32 use std::sync::Once;
33
34 static INIT: Once = Once::new();
35
get_isle_files(name: &str) -> Vec<std::path::PathBuf>36 pub fn get_isle_files(name: &str) -> Vec<std::path::PathBuf> {
37 let cur_dir = env::current_dir().expect("Can't access current working directory");
38 let gen_dir = cur_dir.join("test_output");
39 INIT.call_once(|| {
40 // Logger
41 env_logger::init();
42 // Test directory
43 if !gen_dir.is_dir() {
44 std::fs::create_dir(gen_dir.as_path()).unwrap();
45 }
46 // Generate ISLE files.
47 generate_isle(gen_dir.as_path()).expect("Can't generate ISLE");
48 });
49
50 let codegen_crate_dir = cur_dir.join("../../../codegen");
51 let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle");
52
53 match name {
54 "shared_lower" => {
55 let mut shared = shared_isle_lower_paths(codegen_crate_dir.as_path());
56 shared.push(gen_dir.join("clif_lower.isle"));
57 shared
58 }
59 _ => {
60 // Lookup ISLE shared .
61 let compilations =
62 get_isle_compilations(codegen_crate_dir.as_path(), gen_dir.as_path());
63
64 // Return inputs from the matching compilation, if any.
65 let mut inputs = compilations.lookup(name).unwrap().inputs();
66 inputs.push(inst_specs_isle);
67 inputs
68 }
69 }
70 }
71
72 // Some examples of functions we might need
73 #[allow(dead_code)]
just_8_result() -> TestResult74 pub fn just_8_result() -> TestResult {
75 TestResult::Simple(vec![(Bitwidth::I8, VerificationResult::Success)])
76 }
77
78 #[allow(dead_code)]
just_16_result() -> TestResult79 pub fn just_16_result() -> TestResult {
80 TestResult::Simple(vec![(Bitwidth::I16, VerificationResult::Success)])
81 }
82
83 #[allow(dead_code)]
just_32_result() -> TestResult84 pub fn just_32_result() -> TestResult {
85 TestResult::Simple(vec![(Bitwidth::I32, VerificationResult::Success)])
86 }
87
88 #[allow(dead_code)]
just_64_result() -> TestResult89 pub fn just_64_result() -> TestResult {
90 TestResult::Simple(vec![(Bitwidth::I64, VerificationResult::Success)])
91 }
92
93 /// All bitwidths verify
94 #[allow(dead_code)]
all_success_result() -> Vec<(Bitwidth, VerificationResult)>95 pub fn all_success_result() -> Vec<(Bitwidth, VerificationResult)> {
96 custom_result(&|w| (w, VerificationResult::Success))
97 }
98
99 /// All bitwidths fail
100 #[allow(dead_code)]
all_failure_result() -> Vec<(Bitwidth, VerificationResult)>101 pub fn all_failure_result() -> Vec<(Bitwidth, VerificationResult)> {
102 custom_result(&|w| (w, VerificationResult::Failure(Counterexample {})))
103 }
104
105 /// Specify a custom set expected result (helpful if you want to test all the bitwidths and expect
106 /// a range of different success, failure, and inapplicable outcomes)
custom_result(f: &TestResultBuilder) -> Vec<(Bitwidth, VerificationResult)>107 pub fn custom_result(f: &TestResultBuilder) -> Vec<(Bitwidth, VerificationResult)> {
108 Bitwidth::iter().map(f).collect()
109 }
110
test_rules_with_term(inputs: Vec<PathBuf>, tr: TestResult, config: Config)111 fn test_rules_with_term(inputs: Vec<PathBuf>, tr: TestResult, config: Config) {
112 let (typeenv, termenv, defs) = create_envs(inputs).unwrap();
113 let annotation_env = parse_annotations(&defs, &termenv, &typeenv);
114
115 let term_signatures = annotation_env
116 .get_term_signatures_by_name(&termenv, &typeenv)
117 .get(config.term.as_str())
118 .unwrap_or_else(|| panic!("Missing term type instantiation for {}", config.term))
119 .clone();
120 let instantiations = match tr {
121 TestResult::Simple(s) => {
122 let mut res = vec![];
123 for (width, result) in s {
124 let ty = match width {
125 Bitwidth::I8 => veri_ir::Type::BitVector(Some(8)),
126 Bitwidth::I16 => veri_ir::Type::BitVector(Some(16)),
127 Bitwidth::I32 => veri_ir::Type::BitVector(Some(32)),
128 Bitwidth::I64 => veri_ir::Type::BitVector(Some(64)),
129 };
130 // Find the type instantiations with this as the canonical type
131 let all_instantiations: Vec<&TermSignature> = term_signatures
132 .iter()
133 .filter(|sig| sig.canonical_type.unwrap() == ty)
134 .collect();
135 if all_instantiations.is_empty() {
136 panic!("Missing type instantiation for width {width:?}");
137 }
138 for i in all_instantiations {
139 res.push((i.clone(), result.clone()));
140 }
141 }
142 res
143 }
144 TestResult::Expect(expect) => term_signatures
145 .iter()
146 .map(|sig| (sig.clone(), expect(sig)))
147 .collect(),
148 };
149
150 for (type_instantiation, expected_result) in instantiations {
151 log::debug!("Expected result: {expected_result:?}");
152 let type_sols = type_rules_with_term_and_types(
153 &termenv,
154 &typeenv,
155 &annotation_env,
156 &config,
157 &type_instantiation,
158 &None,
159 );
160 let result = verify_rules_for_term(
161 &termenv,
162 &typeenv,
163 &type_sols,
164 type_instantiation,
165 &None,
166 &config,
167 );
168 assert_eq!(result, expected_result);
169 }
170 }
171
test_from_file_with_lhs_termname_simple( file: &str, termname: String, tr: Vec<(Bitwidth, VerificationResult)>, )172 pub fn test_from_file_with_lhs_termname_simple(
173 file: &str,
174 termname: String,
175 tr: Vec<(Bitwidth, VerificationResult)>,
176 ) {
177 test_from_file_with_lhs_termname(file, termname, TestResult::Simple(tr))
178 }
179
test_from_file_with_lhs_termname(file: &str, termname: String, tr: TestResult)180 pub fn test_from_file_with_lhs_termname(file: &str, termname: String, tr: TestResult) {
181 println!("Verifying {termname} rules in file: {file}");
182 let mut inputs = get_isle_files("shared_lower");
183 inputs.push(PathBuf::from(file));
184 let config = Config {
185 term: termname,
186 distinct_check: true,
187 custom_verification_condition: None,
188 custom_assumptions: None,
189 names: None,
190 };
191 test_rules_with_term(inputs, tr, config);
192 }
193
test_aarch64_rule_with_lhs_termname_simple( rulename: &str, termname: &str, tr: Vec<(Bitwidth, VerificationResult)>, )194 pub fn test_aarch64_rule_with_lhs_termname_simple(
195 rulename: &str,
196 termname: &str,
197 tr: Vec<(Bitwidth, VerificationResult)>,
198 ) {
199 test_aarch64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr))
200 }
201
test_aarch64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult)202 pub fn test_aarch64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) {
203 println!("Verifying rule `{rulename}` with termname {termname} ");
204 let inputs = get_isle_files("aarch64");
205 let config = Config {
206 term: termname.to_string(),
207 distinct_check: true,
208 custom_verification_condition: None,
209 custom_assumptions: None,
210 names: Some(vec![rulename.to_string()]),
211 };
212 test_rules_with_term(inputs, tr, config);
213 }
214
test_x64_rule_with_lhs_termname_simple( rulename: &str, termname: &str, tr: Vec<(Bitwidth, VerificationResult)>, )215 pub fn test_x64_rule_with_lhs_termname_simple(
216 rulename: &str,
217 termname: &str,
218 tr: Vec<(Bitwidth, VerificationResult)>,
219 ) {
220 test_x64_rule_with_lhs_termname(rulename, termname, TestResult::Simple(tr))
221 }
222
test_x64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult)223 pub fn test_x64_rule_with_lhs_termname(rulename: &str, termname: &str, tr: TestResult) {
224 println!("Verifying rule `{rulename}` with termname {termname} ");
225 let inputs = get_isle_files("x64");
226 let config = Config {
227 term: termname.to_string(),
228 distinct_check: true,
229 custom_verification_condition: None,
230 custom_assumptions: None,
231 names: Some(vec![rulename.to_string()]),
232 };
233 test_rules_with_term(inputs, tr, config);
234 }
235
test_from_file_with_config_simple( file: &str, config: Config, tr: Vec<(Bitwidth, VerificationResult)>, )236 pub fn test_from_file_with_config_simple(
237 file: &str,
238 config: Config,
239 tr: Vec<(Bitwidth, VerificationResult)>,
240 ) {
241 test_from_file_with_config(file, config, TestResult::Simple(tr))
242 }
test_from_file_with_config(file: &str, config: Config, tr: TestResult)243 pub fn test_from_file_with_config(file: &str, config: Config, tr: TestResult) {
244 println!("Verifying {} rules in file: {}", config.term, file);
245 let mut inputs = get_isle_files("shared_lower");
246 inputs.push(PathBuf::from(file));
247 test_rules_with_term(inputs, tr, config);
248 }
249
test_aarch64_with_config_simple(config: Config, tr: Vec<(Bitwidth, VerificationResult)>)250 pub fn test_aarch64_with_config_simple(config: Config, tr: Vec<(Bitwidth, VerificationResult)>) {
251 test_aarch64_with_config(config, TestResult::Simple(tr))
252 }
253
test_aarch64_with_config(config: Config, tr: TestResult)254 pub fn test_aarch64_with_config(config: Config, tr: TestResult) {
255 println!(
256 "Verifying rules {:?} with termname {}",
257 config.names, config.term
258 );
259 let inputs = get_isle_files("aarch64");
260 test_rules_with_term(inputs, tr, config);
261 }
262
test_concrete_aarch64_rule_with_lhs_termname( rulename: &str, termname: &str, concrete: ConcreteTest, )263 pub fn test_concrete_aarch64_rule_with_lhs_termname(
264 rulename: &str,
265 termname: &str,
266 concrete: ConcreteTest,
267 ) {
268 println!("Verifying concrete input rule `{rulename}` with termname {termname} ");
269 let inputs = get_isle_files("aarch64");
270 let (typeenv, termenv, defs) = create_envs(inputs).unwrap();
271 let annotation_env = parse_annotations(&defs, &termenv, &typeenv);
272
273 let config = Config {
274 term: termname.to_string(),
275 distinct_check: false,
276 custom_verification_condition: None,
277 custom_assumptions: None,
278 names: Some(vec![rulename.to_string()]),
279 };
280
281 // Get the types/widths for this particular term
282 let args = concrete.args.iter().map(|i| i.ty).collect();
283 let ret = concrete.output.ty;
284 let t = TermSignature {
285 args,
286 ret,
287 canonical_type: None,
288 };
289
290 let type_sols = type_rules_with_term_and_types(
291 &termenv,
292 &typeenv,
293 &annotation_env,
294 &config,
295 &t,
296 &Some(concrete.clone()),
297 );
298 let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config);
299 assert_eq!(result, VerificationResult::Success);
300 }
301
test_concrete_input_from_file_with_lhs_termname( file: &str, termname: String, concrete: ConcreteTest, )302 pub fn test_concrete_input_from_file_with_lhs_termname(
303 file: &str,
304 termname: String,
305 concrete: ConcreteTest,
306 ) {
307 println!("Verifying concrete input {termname} rule in file: {file}");
308 let mut inputs = get_isle_files("shared_lower");
309 inputs.push(PathBuf::from(file));
310
311 let (typeenv, termenv, defs) = create_envs(inputs).unwrap();
312 let annotation_env = parse_annotations(&defs, &termenv, &typeenv);
313
314 let config = Config {
315 term: termname.clone(),
316 distinct_check: false,
317 custom_verification_condition: None,
318 custom_assumptions: None,
319 names: None,
320 };
321
322 // Get the types/widths for this particular term
323 let args = concrete.args.iter().map(|i| i.ty).collect();
324 let ret = concrete.output.ty;
325 let t = TermSignature {
326 args,
327 ret,
328 canonical_type: None,
329 };
330
331 let type_sols = type_rules_with_term_and_types(
332 &termenv,
333 &typeenv,
334 &annotation_env,
335 &config,
336 &t,
337 &Some(concrete.clone()),
338 );
339 let result = verify_rules_for_term(&termenv, &typeenv, &type_sols, t, &Some(concrete), &config);
340 assert_eq!(result, VerificationResult::Success);
341 }
342