blob: 84c28cc4bf4390aeaa2cb66e211f70c648c962ce [file] [log] [blame]
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 <iostream>
#include <cstring>
#include <iomanip>
#include <sstream>
template<typename T1, typename T2> 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::<Vec<_>>()
.join("\n"),
arch_specific_definitions = arch_specific_definitions.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<String>) -> 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::<BTreeMap<&String, String>>()
}
pub fn generate_c_test_loop<T: IntrinsicTypeDefinition + Sized>(
intrinsic: &dyn IntrinsicDefinition<T>,
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<T: IntrinsicTypeDefinition>(
intrinsic: &dyn IntrinsicDefinition<T>,
indentation: Indentation,
constraints: &[&Argument<T>],
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<T: IntrinsicTypeDefinition>(
intrinsic: &dyn IntrinsicDefinition<T>,
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(),
)
}