Files
rust/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

303 lines
8.8 KiB
Rust
Raw Normal View History

use itertools::Itertools;
use std::process::Command;
2025-09-07 13:32:19 +02:00
use crate::common::intrinsic::Intrinsic;
use super::indentation::Indentation;
2025-09-07 13:32:19 +02:00
use super::intrinsic::format_f16_return_value;
use super::intrinsic_helpers::IntrinsicTypeDefinition;
// The number of times each intrinsic will be called.
const PASSES: u32 = 20;
fn write_cargo_toml_header(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> {
2025-07-08 09:59:03 +02:00
writeln!(
w,
concat!(
"[package]\n",
"name = \"{name}\"\n",
2025-07-08 09:59:03 +02:00
"version = \"{version}\"\n",
"authors = [{authors}]\n",
"license = \"{license}\"\n",
"edition = \"2018\"\n",
),
name = name,
2025-07-08 09:59:03 +02:00
version = env!("CARGO_PKG_VERSION"),
authors = env!("CARGO_PKG_AUTHORS")
.split(":")
.format_with(", ", |author, fmt| fmt(&format_args!("\"{author}\""))),
license = env!("CARGO_PKG_LICENSE"),
)
}
pub fn write_bin_cargo_toml(
w: &mut impl std::io::Write,
module_count: usize,
) -> std::io::Result<()> {
write_cargo_toml_header(w, "intrinsic-test-programs")?;
writeln!(w, "[dependencies]")?;
2025-07-08 09:59:03 +02:00
for i in 0..module_count {
writeln!(w, "mod_{i} = {{ path = \"mod_{i}/\" }}")?;
2025-07-08 09:59:03 +02:00
}
Ok(())
}
pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> {
write_cargo_toml_header(w, name)?;
writeln!(w, "[dependencies]")?;
writeln!(w, "core_arch = {{ path = \"../../crates/core_arch\" }}")?;
Ok(())
}
pub fn write_main_rs<'a>(
w: &mut impl std::io::Write,
chunk_count: usize,
cfg: &str,
definitions: &str,
intrinsics: impl Iterator<Item = &'a str> + Clone,
) -> std::io::Result<()> {
writeln!(w, "#![feature(simd_ffi)]")?;
writeln!(w, "#![feature(f16)]")?;
writeln!(w, "#![allow(unused)]")?;
// Cargo will spam the logs if these warnings are not silenced.
writeln!(w, "#![allow(non_upper_case_globals)]")?;
writeln!(w, "#![allow(non_camel_case_types)]")?;
writeln!(w, "#![allow(non_snake_case)]")?;
writeln!(w, "{cfg}")?;
writeln!(w, "{definitions}")?;
for module in 0..chunk_count {
writeln!(w, "use mod_{module}::*;")?;
}
writeln!(w, "fn main() {{")?;
writeln!(w, " match std::env::args().nth(1).unwrap().as_str() {{")?;
for binary in intrinsics {
writeln!(w, " \"{binary}\" => run_{binary}(),")?;
}
writeln!(
w,
" other => panic!(\"unknown intrinsic `{{}}`\", other),"
)?;
writeln!(w, " }}")?;
writeln!(w, "}}")?;
Ok(())
}
pub fn write_lib_rs<T: IntrinsicTypeDefinition>(
w: &mut impl std::io::Write,
notice: &str,
cfg: &str,
definitions: &str,
2025-09-07 13:32:19 +02:00
intrinsics: &[Intrinsic<T>],
) -> std::io::Result<()> {
write!(w, "{notice}")?;
writeln!(w, "#![feature(simd_ffi)]")?;
writeln!(w, "#![feature(f16)]")?;
writeln!(w, "#![allow(unused)]")?;
// Cargo will spam the logs if these warnings are not silenced.
writeln!(w, "#![allow(non_upper_case_globals)]")?;
writeln!(w, "#![allow(non_camel_case_types)]")?;
writeln!(w, "#![allow(non_snake_case)]")?;
writeln!(w, "{cfg}")?;
writeln!(w, "{definitions}")?;
for intrinsic in intrinsics {
crate::common::gen_rust::create_rust_test_module(w, intrinsic)?;
}
Ok(())
}
pub fn compile_rust_programs(toolchain: Option<&str>, target: &str, linker: Option<&str>) -> bool {
/* If there has been a linker explicitly set from the command line then
* we want to set it via setting it in the RUSTFLAGS*/
// This is done because `toolchain` is None when
// the --generate-only flag is passed
if toolchain.is_none() {
return true;
}
trace!("Building cargo command");
2025-07-08 09:59:03 +02:00
let mut cargo_command = Command::new("cargo");
cargo_command.current_dir("rust_programs");
// Do not use the target directory of the workspace please.
cargo_command.env("CARGO_TARGET_DIR", "target");
if toolchain.is_some_and(|val| !val.is_empty()) {
cargo_command.arg(toolchain.unwrap());
2025-07-08 09:59:03 +02:00
}
cargo_command.args(["build", "--target", target, "--release"]);
let mut rust_flags = "-Cdebuginfo=0".to_string();
if let Some(linker) = linker {
rust_flags.push_str(" -C linker=");
rust_flags.push_str(linker);
rust_flags.push_str(" -C link-args=-static");
2025-07-08 09:59:03 +02:00
cargo_command.env("CPPFLAGS", "-fuse-ld=lld");
}
2025-07-08 09:59:03 +02:00
cargo_command.env("RUSTFLAGS", rust_flags);
trace!("running cargo");
if log::log_enabled!(log::Level::Trace) {
cargo_command.stdout(std::process::Stdio::inherit());
cargo_command.stderr(std::process::Stdio::inherit());
}
2025-07-08 09:59:03 +02:00
let output = cargo_command.output();
trace!("cargo is done");
if let Ok(output) = output {
if output.status.success() {
true
} else {
error!(
"Failed to compile code for rust 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 {
2025-07-06 07:15:54 +00:00
error!("Command failed: {output:#?}");
false
}
}
2025-05-26 10:30:23 +05:30
pub fn generate_rust_test_loop<T: IntrinsicTypeDefinition>(
w: &mut impl std::io::Write,
2025-09-07 13:32:19 +02:00
intrinsic: &Intrinsic<T>,
indentation: Indentation,
specializations: &[Vec<u8>],
passes: u32,
) -> std::io::Result<()> {
2025-09-07 13:32:19 +02:00
let intrinsic_name = &intrinsic.name;
// Each function (and each specialization) has its own type. Erase that type with a cast.
let mut coerce = String::from("unsafe fn(");
2025-09-07 13:32:19 +02:00
for _ in intrinsic.arguments.iter().filter(|a| !a.has_constraint()) {
coerce += "_, ";
}
coerce += ") -> _";
match specializations {
[] => {
writeln!(w, " let specializations = [(\"\", {intrinsic_name})];")?;
}
[const_args] if const_args.is_empty() => {
writeln!(w, " let specializations = [(\"\", {intrinsic_name})];")?;
}
_ => {
writeln!(w, " let specializations = [")?;
for specialization in specializations {
let mut specialization: Vec<_> =
specialization.iter().map(|d| d.to_string()).collect();
let const_args = specialization.join(",");
// The identifier is reversed.
specialization.reverse();
let id = specialization.join("-");
writeln!(
w,
" (\"-{id}\", {intrinsic_name}::<{const_args}> as {coerce}),"
)?;
}
writeln!(w, " ];")?;
}
}
let return_value = format_f16_return_value(intrinsic);
let indentation2 = indentation.nested();
let indentation3 = indentation2.nested();
writeln!(
w,
"\
for (id, f) in specializations {{\n\
for i in 0..{passes} {{\n\
unsafe {{\n\
{loaded_args}\
let __return_value = f({args});\n\
println!(\"Result {{id}}-{{}}: {{:?}}\", i + 1, {return_value});\n\
}}\n\
}}\n\
}}",
2025-09-07 13:32:19 +02:00
loaded_args = intrinsic.arguments.load_values_rust(indentation3),
args = intrinsic.arguments.as_call_param_rust(),
)
}
/// Generate the specializations (unique sequences of const-generic arguments) for this intrinsic.
2025-09-12 11:50:25 +00:00
fn generate_rust_specializations(
constraints: &mut impl Iterator<Item = impl Iterator<Item = i64>>,
) -> Vec<Vec<u8>> {
let mut specializations = vec![vec![]];
for constraint in constraints {
specializations = constraint
.flat_map(|right| {
specializations.iter().map(move |left| {
let mut left = left.clone();
left.push(u8::try_from(right).unwrap());
left
})
})
.collect();
}
specializations
}
2025-05-26 10:30:23 +05:30
// Top-level function to create complete test program
pub fn create_rust_test_module<T: IntrinsicTypeDefinition>(
w: &mut impl std::io::Write,
2025-09-07 13:32:19 +02:00
intrinsic: &Intrinsic<T>,
) -> std::io::Result<()> {
2025-09-07 13:32:19 +02:00
trace!("generating `{}`", intrinsic.name);
let indentation = Indentation::default();
2025-09-07 13:32:19 +02:00
writeln!(w, "pub fn run_{}() {{", intrinsic.name)?;
// Define the arrays of arguments.
2025-09-07 13:32:19 +02:00
let arguments = &intrinsic.arguments;
arguments.gen_arglists_rust(w, indentation.nested(), PASSES)?;
// Define any const generics as `const` items, then generate the actual test loop.
let specializations = generate_rust_specializations(
&mut arguments
.iter()
.filter_map(|i| i.constraint.as_ref().map(|v| v.iter())),
);
generate_rust_test_loop(w, intrinsic, indentation, &specializations, PASSES)?;
writeln!(w, "}}")?;
Ok(())
}