blob: b2d242ab73602c24c1e14a88376100eab8a39e2e [file] [log] [blame]
use super::argument::ArgumentList;
use super::format::Indentation;
use super::types::{IntrinsicType, TypeKind};
/// An intrinsic
#[derive(Debug, PartialEq, Clone)]
pub struct Intrinsic {
/// The function name of this intrinsic.
pub name: String,
/// Any arguments for this intrinsic.
pub arguments: ArgumentList,
/// The return type of this intrinsic.
pub results: IntrinsicType,
/// Whether this intrinsic is only available on A64.
pub a64_only: bool,
}
impl Intrinsic {
/// Generates a std::cout for the intrinsics results that will match the
/// rust debug output format for the return type. The generated line assumes
/// there is an int i in scope which is the current pass number.
pub fn print_result_c(&self, indentation: Indentation, additional: &str) -> String {
let lanes = if self.results.num_vectors() > 1 {
(0..self.results.num_vectors())
.map(|vector| {
format!(
r#""{ty}(" << {lanes} << ")""#,
ty = self.results.c_single_vector_type(),
lanes = (0..self.results.num_lanes())
.map(move |idx| -> std::string::String {
format!(
"{cast}{lane_fn}(__return_value.val[{vector}], {lane})",
cast = self.results.c_promotion(),
lane_fn = self.results.get_lane_function(),
lane = idx,
vector = vector,
)
})
.collect::<Vec<_>>()
.join(r#" << ", " << "#)
)
})
.collect::<Vec<_>>()
.join(r#" << ", " << "#)
} else if self.results.num_lanes() > 1 {
(0..self.results.num_lanes())
.map(|idx| -> std::string::String {
format!(
"{cast}{lane_fn}(__return_value, {lane})",
cast = self.results.c_promotion(),
lane_fn = self.results.get_lane_function(),
lane = idx
)
})
.collect::<Vec<_>>()
.join(r#" << ", " << "#)
} else {
format!(
"{promote}cast<{cast}>(__return_value)",
cast = match self.results.kind() {
TypeKind::Float if self.results.inner_size() == 16 => "float16_t".to_string(),
TypeKind::Float if self.results.inner_size() == 32 => "float".to_string(),
TypeKind::Float if self.results.inner_size() == 64 => "double".to_string(),
TypeKind::Int => format!("int{}_t", self.results.inner_size()),
TypeKind::UInt => format!("uint{}_t", self.results.inner_size()),
TypeKind::Poly => format!("poly{}_t", self.results.inner_size()),
ty => todo!("print_result_c - Unknown type: {:#?}", ty),
},
promote = self.results.c_promotion(),
)
};
format!(
r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
ty = if self.results.is_simd() {
format!("{}(", self.results.c_type())
} else {
String::from("")
},
close = if self.results.is_simd() { ")" } else { "" },
)
}
pub fn generate_loop_c(
&self,
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 = self.arguments.load_values_c(body_indentation),
intrinsic_call = self.name,
args = self.arguments.as_call_param_c(),
print_result = self.print_result_c(body_indentation, additional)
)
}
pub fn generate_loop_rust(
&self,
indentation: Indentation,
additional: &str,
passes: u32,
) -> String {
let constraints = self.arguments.as_constraint_parameters_rust();
let constraints = if !constraints.is_empty() {
format!("::<{constraints}>")
} else {
constraints
};
// the `intrinsic-test` crate compares the output of C and Rust intrinsics. Currently, It uses
// a string representation of the output value to compare. In C, f16 values are currently printed
// as hexadecimal integers. Since https://github.com/rust-lang/rust/pull/127013, rust does print
// them as decimal floating point values. To keep the intrinsics tests working, for now, format
// vectors containing f16 values like C prints them.
let return_value = match self.results.kind() {
TypeKind::Float if self.results.inner_size() == 16 => "debug_f16(__return_value)",
_ => "format_args!(\"{__return_value:.150?}\")",
};
let indentation2 = indentation.nested();
let indentation3 = indentation2.nested();
format!(
"{indentation}for i in 0..{passes} {{\n\
{indentation2}unsafe {{\n\
{loaded_args}\
{indentation3}let __return_value = {intrinsic_call}{const}({args});\n\
{indentation3}println!(\"Result {additional}-{{}}: {{:?}}\", i + 1, {return_value});\n\
{indentation2}}}\n\
{indentation}}}",
loaded_args = self.arguments.load_values_rust(indentation3),
intrinsic_call = self.name,
const = constraints,
args = self.arguments.as_call_param_rust(),
)
}
}