use itertools::Itertools; use rayon::prelude::*; use std::collections::BTreeMap; use std::process::Command; use super::argument::Argument; use super::indentation::Indentation; use super::intrinsic::IntrinsicDefinition; use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. const PASSES: u32 = 20; // Formats the main C program template with placeholders pub fn format_c_main_template( notices: &str, header_files: &[&str], arch_identifier: &str, arch_specific_definitions: &[&str], arglists: &str, passes: &str, ) -> String { format!( r#"{notices}{header_files} #include #include #include #include template T1 cast(T2 x) {{ static_assert(sizeof(T1) == sizeof(T2), "sizeof T1 and T2 must be the same"); T1 ret{{}}; memcpy(&ret, &x, sizeof(T1)); return ret; }} std::ostream& operator<<(std::ostream& os, float16_t value) {{ uint16_t temp = 0; memcpy(&temp, &value, sizeof(float16_t)); std::stringstream ss; ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << temp; os << ss.str(); return os; }} #ifdef __{arch_identifier}__ {arch_specific_definitions} #endif {arglists} int main(int argc, char **argv) {{ {passes} return 0; }}"#, header_files = header_files .iter() .map(|header| format!("#include <{header}>")) .collect::>() .join("\n"), arch_specific_definitions = arch_specific_definitions.into_iter().join("\n"), ) } pub fn compile_c_programs(compiler_commands: &[String]) -> bool { compiler_commands .par_iter() .map(|compiler_command| { let output = Command::new("sh").arg("-c").arg(compiler_command).output(); if let Ok(output) = output { if output.status.success() { true } else { error!( "Failed to compile code for intrinsics: \n\nstdout:\n{}\n\nstderr:\n{}", std::str::from_utf8(&output.stdout).unwrap_or(""), std::str::from_utf8(&output.stderr).unwrap_or("") ); false } } else { error!("Command failed: {:#?}", output); false } }) .find_any(|x| !x) .is_none() } // Creates directory structure and file path mappings pub fn setup_c_file_paths(identifiers: &Vec) -> BTreeMap<&String, String> { let _ = std::fs::create_dir("c_programs"); identifiers .par_iter() .map(|identifier| { let c_filename = format!(r#"c_programs/{identifier}.cpp"#); (identifier, c_filename) }) .collect::>() } pub fn generate_c_test_loop( intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, additional: &str, passes: u32, _target: &str, ) -> String { let body_indentation = indentation.nested(); format!( "{indentation}for (int i=0; i<{passes}; i++) {{\n\ {loaded_args}\ {body_indentation}auto __return_value = {intrinsic_call}({args});\n\ {print_result}\n\ {indentation}}}", loaded_args = intrinsic.arguments().load_values_c(body_indentation), intrinsic_call = intrinsic.name(), args = intrinsic.arguments().as_call_param_c(), print_result = intrinsic.print_result_c(body_indentation, additional) ) } pub fn generate_c_constraint_blocks( intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, constraints: &[&Argument], name: String, target: &str, ) -> String { if let Some((current, constraints)) = constraints.split_last() { let range = current .constraint .iter() .map(|c| c.to_range()) .flat_map(|r| r.into_iter()); let body_indentation = indentation.nested(); range .map(|i| { format!( "{indentation}{{\n\ {body_indentation}{ty} {name} = {val};\n\ {pass}\n\ {indentation}}}", name = current.name, ty = current.ty.c_type(), val = i, pass = generate_c_constraint_blocks( intrinsic, body_indentation, constraints, format!("{name}-{i}"), target, ) ) }) .join("\n") } else { generate_c_test_loop(intrinsic, indentation, &name, PASSES, target) } } // Compiles C test programs using specified compiler pub fn create_c_test_program( intrinsic: &dyn IntrinsicDefinition, header_files: &[&str], target: &str, c_target: &str, notices: &str, arch_specific_definitions: &[&str], ) -> String { let arguments = intrinsic.arguments(); let constraints = arguments .iter() .filter(|&i| i.has_constraint()) .collect_vec(); let indentation = Indentation::default(); format_c_main_template( notices, header_files, c_target, arch_specific_definitions, intrinsic .arguments() .gen_arglists_c(indentation, PASSES) .as_str(), generate_c_constraint_blocks( intrinsic, indentation.nested(), constraints.as_slice(), Default::default(), target, ) .as_str(), ) }