about summary refs log tree commit diff
path: root/library/stdarch/crates/intrinsic-test/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/stdarch/crates/intrinsic-test/src')
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/compile.rs64
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/config.rs122
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs95
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs137
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/mod.rs124
-rw-r--r--library/stdarch/crates/intrinsic-test/src/arm/types.rs195
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/argument.rs209
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/cli.rs113
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/compare.rs90
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/compile_c.rs154
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/constraint.rs17
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/gen_c.rs198
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs243
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/indentation.rs22
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs51
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs296
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/mod.rs25
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/values.rs120
-rw-r--r--library/stdarch/crates/intrinsic-test/src/common/write_file.rs66
-rw-r--r--library/stdarch/crates/intrinsic-test/src/main.rs42
20 files changed, 2383 insertions, 0 deletions
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/compile.rs b/library/stdarch/crates/intrinsic-test/src/arm/compile.rs
new file mode 100644
index 00000000000..8276cd87c1c
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/compile.rs
@@ -0,0 +1,64 @@
+use crate::common::compile_c::CompilationCommandBuilder;
+use crate::common::gen_c::compile_c_programs;
+
+pub fn compile_c_arm(
+    intrinsics_name_list: &[String],
+    compiler: &str,
+    target: &str,
+    cxx_toolchain_dir: Option<&str>,
+) -> bool {
+    // -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations
+    let mut command = CompilationCommandBuilder::new()
+        .add_arch_flags(vec!["armv8.6-a", "crypto", "crc", "dotprod", "fp16"])
+        .set_compiler(compiler)
+        .set_target(target)
+        .set_opt_level("2")
+        .set_cxx_toolchain_dir(cxx_toolchain_dir)
+        .set_project_root("c_programs")
+        .add_extra_flags(vec!["-ffp-contract=off", "-Wno-narrowing"]);
+
+    if !target.contains("v7") {
+        command = command.add_arch_flags(vec!["faminmax", "lut", "sha3"]);
+    }
+
+    /*
+     * clang++ cannot link an aarch64_be object file, so we invoke
+     * aarch64_be-unknown-linux-gnu's C++ linker. This ensures that we
+     * are testing the intrinsics against LLVM.
+     *
+     * Note: setting `--sysroot=<...>` which is the obvious thing to do
+     * does not work as it gets caught up with `#include_next <stdlib.h>`
+     * not existing...
+     */
+    if target.contains("aarch64_be") {
+        command = command
+            .set_linker(
+                cxx_toolchain_dir.unwrap_or("").to_string() + "/bin/aarch64_be-none-linux-gnu-g++",
+            )
+            .set_include_paths(vec![
+                "/include",
+                "/aarch64_be-none-linux-gnu/include",
+                "/aarch64_be-none-linux-gnu/include/c++/14.2.1",
+                "/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu",
+                "/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward",
+                "/aarch64_be-none-linux-gnu/libc/usr/include",
+            ]);
+    }
+
+    if !compiler.contains("clang") {
+        command = command.add_extra_flag("-flax-vector-conversions");
+    }
+
+    let compiler_commands = intrinsics_name_list
+        .iter()
+        .map(|intrinsic_name| {
+            command
+                .clone()
+                .set_input_name(intrinsic_name)
+                .set_output_name(intrinsic_name)
+                .make_string()
+        })
+        .collect::<Vec<_>>();
+
+    compile_c_programs(&compiler_commands)
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs
new file mode 100644
index 00000000000..cee80374ae9
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs
@@ -0,0 +1,122 @@
+pub fn build_notices(line_prefix: &str) -> String {
+    format!(
+        "\
+{line_prefix}This is a transient test file, not intended for distribution. Some aspects of the
+{line_prefix}test are derived from a JSON specification, published under the same license as the
+{line_prefix}`intrinsic-test` crate.\n
+"
+    )
+}
+
+pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& os, poly128_t value) {
+    std::stringstream temp;
+    do {
+      int n = value % 10;
+      value /= 10;
+      temp << n;
+    } while (value != 0);
+    std::string tempstr(temp.str());
+    std::string res(tempstr.rbegin(), tempstr.rend());
+    os << res;
+    return os;
+}"#;
+
+// Format f16 values (and vectors containing them) in a way that is consistent with C.
+pub const F16_FORMATTING_DEF: &str = r#"
+/// Used to continue `Debug`ging SIMD types as `MySimd(1, 2, 3, 4)`, as they
+/// were before moving to array-based simd.
+#[inline]
+fn debug_simd_finish<T: core::fmt::Debug, const N: usize>(
+    formatter: &mut core::fmt::Formatter<'_>,
+    type_name: &str,
+    array: &[T; N],
+) -> core::fmt::Result {
+    core::fmt::Formatter::debug_tuple_fields_finish(
+        formatter,
+        type_name,
+        &core::array::from_fn::<&dyn core::fmt::Debug, N, _>(|i| &array[i]),
+    )
+}
+
+#[repr(transparent)]
+struct Hex<T>(T);
+
+impl<T: DebugHexF16> core::fmt::Debug for Hex<T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        <T as DebugHexF16>::fmt(&self.0, f)
+    }
+}
+
+fn debug_f16<T: DebugHexF16>(x: T) -> impl core::fmt::Debug {
+    Hex(x)
+}
+
+trait DebugHexF16 {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
+}
+
+impl DebugHexF16 for f16 {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "{:#06x?}", self.to_bits())
+    }
+}
+
+impl DebugHexF16 for float16x4_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        let array = unsafe { core::mem::transmute::<_, [Hex<f16>; 4]>(*self) };
+        debug_simd_finish(f, "float16x4_t", &array)
+    }
+}
+
+impl DebugHexF16 for float16x8_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        let array = unsafe { core::mem::transmute::<_, [Hex<f16>; 8]>(*self) };
+        debug_simd_finish(f, "float16x8_t", &array)
+    }
+}
+
+impl DebugHexF16 for float16x4x2_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x4x2_t", &[Hex(self.0), Hex(self.1)])
+    }
+}
+impl DebugHexF16 for float16x4x3_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x4x3_t", &[Hex(self.0), Hex(self.1), Hex(self.2)])
+    }
+}
+impl DebugHexF16 for float16x4x4_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x4x4_t", &[Hex(self.0), Hex(self.1), Hex(self.2), Hex(self.3)])
+    }
+}
+
+impl DebugHexF16 for float16x8x2_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x8x2_t", &[Hex(self.0), Hex(self.1)])
+    }
+}
+impl DebugHexF16 for float16x8x3_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x8x3_t", &[Hex(self.0), Hex(self.1), Hex(self.2)])
+    }
+}
+impl DebugHexF16 for float16x8x4_t {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        debug_simd_finish(f, "float16x8x4_t", &[Hex(self.0), Hex(self.1), Hex(self.2), Hex(self.3)])
+    }
+}
+ "#;
+
+pub const AARCH_CONFIGURATIONS: &str = r#"
+#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))]
+#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_dotprod))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sha3))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))]
+#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))]
+#![feature(fmt_helpers_for_derive)]
+#![feature(stdarch_neon_f16)]
+"#;
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs
new file mode 100644
index 00000000000..773dabf4d75
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs
@@ -0,0 +1,95 @@
+use crate::common::argument::ArgumentList;
+use crate::common::indentation::Indentation;
+use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
+use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind};
+use std::ops::Deref;
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ArmIntrinsicType(pub IntrinsicType);
+
+impl Deref for ArmIntrinsicType {
+    type Target = IntrinsicType;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl IntrinsicDefinition<ArmIntrinsicType> for Intrinsic<ArmIntrinsicType> {
+    fn arguments(&self) -> ArgumentList<ArmIntrinsicType> {
+        self.arguments.clone()
+    }
+
+    fn results(&self) -> ArmIntrinsicType {
+        self.results.clone()
+    }
+
+    fn name(&self) -> String {
+        self.name.clone()
+    }
+
+    /// 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.
+    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 { "" },
+        )
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs
new file mode 100644
index 00000000000..0ac47484b01
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs
@@ -0,0 +1,137 @@
+use super::intrinsic::ArmIntrinsicType;
+use crate::common::argument::{Argument, ArgumentList};
+use crate::common::constraint::Constraint;
+use crate::common::intrinsic::Intrinsic;
+use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition};
+use serde::Deserialize;
+use serde_json::Value;
+use std::collections::HashMap;
+use std::path::Path;
+
+#[derive(Deserialize, Debug)]
+#[serde(deny_unknown_fields)]
+struct ReturnType {
+    value: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(untagged, deny_unknown_fields)]
+pub enum ArgPrep {
+    Register {
+        #[serde(rename = "register")]
+        #[allow(dead_code)]
+        reg: String,
+    },
+    Immediate {
+        #[serde(rename = "minimum")]
+        min: i64,
+        #[serde(rename = "maximum")]
+        max: i64,
+    },
+    Nothing {},
+}
+
+impl TryFrom<Value> for ArgPrep {
+    type Error = serde_json::Error;
+
+    fn try_from(value: Value) -> Result<Self, Self::Error> {
+        serde_json::from_value(value)
+    }
+}
+
+#[derive(Deserialize, Debug)]
+struct JsonIntrinsic {
+    #[serde(rename = "SIMD_ISA")]
+    simd_isa: String,
+    name: String,
+    arguments: Vec<String>,
+    return_type: ReturnType,
+    #[serde(rename = "Arguments_Preparation")]
+    args_prep: Option<HashMap<String, Value>>,
+    #[serde(rename = "Architectures")]
+    architectures: Vec<String>,
+}
+
+pub fn get_neon_intrinsics(
+    filename: &Path,
+    target: &str,
+) -> Result<Vec<Intrinsic<ArmIntrinsicType>>, Box<dyn std::error::Error>> {
+    let file = std::fs::File::open(filename)?;
+    let reader = std::io::BufReader::new(file);
+    let json: Vec<JsonIntrinsic> = serde_json::from_reader(reader).expect("Couldn't parse JSON");
+
+    let parsed = json
+        .into_iter()
+        .filter_map(|intr| {
+            if intr.simd_isa == "Neon" {
+                Some(json_to_intrinsic(intr, target).expect("Couldn't parse JSON"))
+            } else {
+                None
+            }
+        })
+        .collect();
+    Ok(parsed)
+}
+
+fn json_to_intrinsic(
+    mut intr: JsonIntrinsic,
+    target: &str,
+) -> Result<Intrinsic<ArmIntrinsicType>, Box<dyn std::error::Error>> {
+    let name = intr.name.replace(['[', ']'], "");
+
+    let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?;
+
+    let args = intr
+        .arguments
+        .into_iter()
+        .enumerate()
+        .map(|(i, arg)| {
+            let arg_name = Argument::<ArmIntrinsicType>::type_and_name_from_c(&arg).1;
+            let metadata = intr.args_prep.as_mut();
+            let metadata = metadata.and_then(|a| a.remove(arg_name));
+            let arg_prep: Option<ArgPrep> = metadata.and_then(|a| a.try_into().ok());
+            let constraint: Option<Constraint> = arg_prep.and_then(|a| a.try_into().ok());
+
+            let mut arg = Argument::<ArmIntrinsicType>::from_c(i, &arg, target, constraint);
+
+            // The JSON doesn't list immediates as const
+            let IntrinsicType {
+                ref mut constant, ..
+            } = arg.ty.0;
+            if arg.name.starts_with("imm") {
+                *constant = true
+            }
+            arg
+        })
+        .collect();
+
+    let arguments = ArgumentList::<ArmIntrinsicType> { args };
+
+    Ok(Intrinsic {
+        name,
+        arguments,
+        results: *results,
+        arch_tags: intr.architectures,
+    })
+}
+
+/// ARM-specific
+impl TryFrom<ArgPrep> for Constraint {
+    type Error = ();
+
+    fn try_from(prep: ArgPrep) -> Result<Self, Self::Error> {
+        let parsed_ints = match prep {
+            ArgPrep::Immediate { min, max } => Ok((min, max)),
+            _ => Err(()),
+        };
+        if let Ok((min, max)) = parsed_ints {
+            if min == max {
+                Ok(Constraint::Equal(min))
+            } else {
+                Ok(Constraint::Range(min..max + 1))
+            }
+        } else {
+            Err(())
+        }
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs
new file mode 100644
index 00000000000..6aaa49ff97f
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs
@@ -0,0 +1,124 @@
+mod compile;
+mod config;
+mod intrinsic;
+mod json_parser;
+mod types;
+
+use crate::common::SupportedArchitectureTest;
+use crate::common::cli::ProcessedCli;
+use crate::common::compare::compare_outputs;
+use crate::common::gen_rust::compile_rust_programs;
+use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
+use crate::common::intrinsic_helpers::TypeKind;
+use crate::common::write_file::{write_c_testfiles, write_rust_testfiles};
+use compile::compile_c_arm;
+use config::{AARCH_CONFIGURATIONS, F16_FORMATTING_DEF, POLY128_OSTREAM_DEF, build_notices};
+use intrinsic::ArmIntrinsicType;
+use json_parser::get_neon_intrinsics;
+
+pub struct ArmArchitectureTest {
+    intrinsics: Vec<Intrinsic<ArmIntrinsicType>>,
+    cli_options: ProcessedCli,
+}
+
+impl SupportedArchitectureTest for ArmArchitectureTest {
+    fn create(cli_options: ProcessedCli) -> Box<Self> {
+        let a32 = cli_options.target.contains("v7");
+        let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target)
+            .expect("Error parsing input file");
+
+        intrinsics.sort_by(|a, b| a.name.cmp(&b.name));
+
+        let mut intrinsics = intrinsics
+            .into_iter()
+            // Not sure how we would compare intrinsic that returns void.
+            .filter(|i| i.results.kind() != TypeKind::Void)
+            .filter(|i| i.results.kind() != TypeKind::BFloat)
+            .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat))
+            // Skip pointers for now, we would probably need to look at the return
+            // type to work out how many elements we need to point to.
+            .filter(|i| !i.arguments.iter().any(|a| a.is_ptr()))
+            .filter(|i| !i.arguments.iter().any(|a| a.ty.inner_size() == 128))
+            .filter(|i| !cli_options.skip.contains(&i.name))
+            .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()]))
+            .collect::<Vec<_>>();
+        intrinsics.dedup();
+
+        Box::new(Self {
+            intrinsics,
+            cli_options,
+        })
+    }
+
+    fn build_c_file(&self) -> bool {
+        let compiler = self.cli_options.cpp_compiler.as_deref();
+        let target = &self.cli_options.target;
+        let cxx_toolchain_dir = self.cli_options.cxx_toolchain_dir.as_deref();
+        let c_target = "aarch64";
+
+        let intrinsics_name_list = write_c_testfiles(
+            &self
+                .intrinsics
+                .iter()
+                .map(|i| i as &dyn IntrinsicDefinition<_>)
+                .collect::<Vec<_>>(),
+            target,
+            c_target,
+            &["arm_neon.h", "arm_acle.h", "arm_fp16.h"],
+            &build_notices("// "),
+            &[POLY128_OSTREAM_DEF],
+        );
+
+        match compiler {
+            None => true,
+            Some(compiler) => compile_c_arm(
+                intrinsics_name_list.as_slice(),
+                compiler,
+                target,
+                cxx_toolchain_dir,
+            ),
+        }
+    }
+
+    fn build_rust_file(&self) -> bool {
+        let rust_target = if self.cli_options.target.contains("v7") {
+            "arm"
+        } else {
+            "aarch64"
+        };
+        let target = &self.cli_options.target;
+        let toolchain = self.cli_options.toolchain.as_deref();
+        let linker = self.cli_options.linker.as_deref();
+        let intrinsics_name_list = write_rust_testfiles(
+            self.intrinsics
+                .iter()
+                .map(|i| i as &dyn IntrinsicDefinition<_>)
+                .collect::<Vec<_>>(),
+            rust_target,
+            &build_notices("// "),
+            F16_FORMATTING_DEF,
+            AARCH_CONFIGURATIONS,
+        );
+
+        compile_rust_programs(intrinsics_name_list, toolchain, target, linker)
+    }
+
+    fn compare_outputs(&self) -> bool {
+        if let Some(ref toolchain) = self.cli_options.toolchain {
+            let intrinsics_name_list = self
+                .intrinsics
+                .iter()
+                .map(|i| i.name.clone())
+                .collect::<Vec<_>>();
+
+            compare_outputs(
+                &intrinsics_name_list,
+                toolchain,
+                &self.cli_options.c_runner,
+                &self.cli_options.target,
+            )
+        } else {
+            true
+        }
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs
new file mode 100644
index 00000000000..9f3d6302f46
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs
@@ -0,0 +1,195 @@
+use super::intrinsic::ArmIntrinsicType;
+use crate::common::cli::Language;
+use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind};
+
+impl IntrinsicTypeDefinition for ArmIntrinsicType {
+    /// Gets a string containing the typename for this type in C format.
+    fn c_type(&self) -> String {
+        let prefix = self.0.kind.c_prefix();
+        let const_prefix = if self.0.constant { "const " } else { "" };
+
+        if let (Some(bit_len), simd_len, vec_len) =
+            (self.0.bit_len, self.0.simd_len, self.0.vec_len)
+        {
+            match (simd_len, vec_len) {
+                (None, None) => format!("{const_prefix}{prefix}{bit_len}_t"),
+                (Some(simd), None) => format!("{prefix}{bit_len}x{simd}_t"),
+                (Some(simd), Some(vec)) => format!("{prefix}{bit_len}x{simd}x{vec}_t"),
+                (None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
+            }
+        } else {
+            todo!("{:#?}", self)
+        }
+    }
+
+    fn c_single_vector_type(&self) -> String {
+        if let (Some(bit_len), Some(simd_len)) = (self.0.bit_len, self.0.simd_len) {
+            format!(
+                "{prefix}{bit_len}x{simd_len}_t",
+                prefix = self.0.kind.c_prefix()
+            )
+        } else {
+            unreachable!("Shouldn't be called on this type")
+        }
+    }
+
+    fn rust_type(&self) -> String {
+        let rust_prefix = self.0.kind.rust_prefix();
+        let c_prefix = self.0.kind.c_prefix();
+        if self.0.ptr_constant {
+            self.c_type()
+        } else if let (Some(bit_len), simd_len, vec_len) =
+            (self.0.bit_len, self.0.simd_len, self.0.vec_len)
+        {
+            match (simd_len, vec_len) {
+                (None, None) => format!("{rust_prefix}{bit_len}"),
+                (Some(simd), None) => format!("{c_prefix}{bit_len}x{simd}_t"),
+                (Some(simd), Some(vec)) => format!("{c_prefix}{bit_len}x{simd}x{vec}_t"),
+                (None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
+            }
+        } else {
+            todo!("{:#?}", self)
+        }
+    }
+
+    /// Determines the load function for this type.
+    fn get_load_function(&self, language: Language) -> String {
+        if let IntrinsicType {
+            kind: k,
+            bit_len: Some(bl),
+            simd_len,
+            vec_len,
+            target,
+            ..
+        } = &self.0
+        {
+            let quad = if simd_len.unwrap_or(1) * bl > 64 {
+                "q"
+            } else {
+                ""
+            };
+
+            let choose_workaround = language == Language::C && target.contains("v7");
+            format!(
+                "vld{len}{quad}_{type}{size}",
+                type = match k {
+                    TypeKind::UInt => "u",
+                    TypeKind::Int => "s",
+                    TypeKind::Float => "f",
+                    // The ACLE doesn't support 64-bit polynomial loads on Armv7
+                    // if armv7 and bl == 64, use "s", else "p"
+                    TypeKind::Poly => if choose_workaround && *bl == 64 {"s"} else {"p"},
+                    x => todo!("get_load_function TypeKind: {:#?}", x),
+                },
+                size = bl,
+                quad = quad,
+                len = vec_len.unwrap_or(1),
+            )
+        } else {
+            todo!("get_load_function IntrinsicType: {:#?}", self)
+        }
+    }
+
+    /// Determines the get lane function for this type.
+    fn get_lane_function(&self) -> String {
+        if let IntrinsicType {
+            kind: k,
+            bit_len: Some(bl),
+            simd_len,
+            ..
+        } = &self.0
+        {
+            let quad = if (simd_len.unwrap_or(1) * bl) > 64 {
+                "q"
+            } else {
+                ""
+            };
+            format!(
+                "vget{quad}_lane_{type}{size}",
+                type = match k {
+                    TypeKind::UInt => "u",
+                    TypeKind::Int => "s",
+                    TypeKind::Float => "f",
+                    TypeKind::Poly => "p",
+                    x => todo!("get_load_function TypeKind: {:#?}", x),
+                },
+                size = bl,
+                quad = quad,
+            )
+        } else {
+            todo!("get_lane_function IntrinsicType: {:#?}", self)
+        }
+    }
+
+    fn from_c(s: &str, target: &str) -> Result<Box<Self>, String> {
+        const CONST_STR: &str = "const";
+        if let Some(s) = s.strip_suffix('*') {
+            let (s, constant) = match s.trim().strip_suffix(CONST_STR) {
+                Some(stripped) => (stripped, true),
+                None => (s, false),
+            };
+            let s = s.trim_end();
+            let temp_return = ArmIntrinsicType::from_c(s, target);
+            temp_return.map(|mut op| {
+                let edited = op.as_mut();
+                edited.0.ptr = true;
+                edited.0.ptr_constant = constant;
+                op
+            })
+        } else {
+            // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t]
+            let (mut s, constant) = match s.strip_prefix(CONST_STR) {
+                Some(stripped) => (stripped.trim(), true),
+                None => (s, false),
+            };
+            s = s.strip_suffix("_t").unwrap_or(s);
+            let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ]
+            let start = parts.next().ok_or("Impossible to parse type")?;
+            if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) {
+                let (arg_kind, bit_len) = start.split_at(digit_start);
+                let arg_kind = arg_kind.parse::<TypeKind>()?;
+                let bit_len = bit_len.parse::<u32>().map_err(|err| err.to_string())?;
+                let simd_len = match parts.next() {
+                    Some(part) => Some(
+                        part.parse::<u32>()
+                            .map_err(|_| "Couldn't parse simd_len: {part}")?,
+                    ),
+                    None => None,
+                };
+                let vec_len = match parts.next() {
+                    Some(part) => Some(
+                        part.parse::<u32>()
+                            .map_err(|_| "Couldn't parse vec_len: {part}")?,
+                    ),
+                    None => None,
+                };
+                Ok(Box::new(ArmIntrinsicType(IntrinsicType {
+                    ptr: false,
+                    ptr_constant: false,
+                    constant,
+                    kind: arg_kind,
+                    bit_len: Some(bit_len),
+                    simd_len,
+                    vec_len,
+                    target: target.to_string(),
+                })))
+            } else {
+                let kind = start.parse::<TypeKind>()?;
+                let bit_len = match kind {
+                    TypeKind::Int => Some(32),
+                    _ => None,
+                };
+                Ok(Box::new(ArmIntrinsicType(IntrinsicType {
+                    ptr: false,
+                    ptr_constant: false,
+                    constant,
+                    kind: start.parse::<TypeKind>()?,
+                    bit_len,
+                    simd_len: None,
+                    vec_len: None,
+                    target: target.to_string(),
+                })))
+            }
+        }
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs
new file mode 100644
index 00000000000..443ccb919f4
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs
@@ -0,0 +1,209 @@
+use super::cli::Language;
+use super::constraint::Constraint;
+use super::indentation::Indentation;
+use super::intrinsic_helpers::IntrinsicTypeDefinition;
+
+/// An argument for the intrinsic.
+#[derive(Debug, PartialEq, Clone)]
+pub struct Argument<T: IntrinsicTypeDefinition> {
+    /// The argument's index in the intrinsic function call.
+    pub pos: usize,
+    /// The argument name.
+    pub name: String,
+    /// The type of the argument.
+    pub ty: T,
+    /// Any constraints that are on this argument
+    pub constraint: Option<Constraint>,
+}
+
+impl<T> Argument<T>
+where
+    T: IntrinsicTypeDefinition,
+{
+    pub fn to_c_type(&self) -> String {
+        self.ty.c_type()
+    }
+
+    pub fn is_simd(&self) -> bool {
+        self.ty.is_simd()
+    }
+
+    pub fn is_ptr(&self) -> bool {
+        self.ty.is_ptr()
+    }
+
+    pub fn has_constraint(&self) -> bool {
+        self.constraint.is_some()
+    }
+
+    pub fn type_and_name_from_c(arg: &str) -> (&str, &str) {
+        let split_index = arg
+            .rfind([' ', '*'])
+            .expect("Couldn't split type and argname");
+
+        (arg[..split_index + 1].trim_end(), &arg[split_index + 1..])
+    }
+
+    /// The binding keyword (e.g. "const" or "let") for the array of possible test inputs.
+    fn rust_vals_array_binding(&self) -> impl std::fmt::Display {
+        if self.ty.is_rust_vals_array_const() {
+            "const"
+        } else {
+            "let"
+        }
+    }
+
+    /// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs.
+    fn rust_vals_array_name(&self) -> impl std::fmt::Display {
+        if self.ty.is_rust_vals_array_const() {
+            format!("{}_VALS", self.name.to_uppercase())
+        } else {
+            format!("{}_vals", self.name.to_lowercase())
+        }
+    }
+
+    pub fn from_c(
+        pos: usize,
+        arg: &str,
+        target: &str,
+        constraint: Option<Constraint>,
+    ) -> Argument<T> {
+        let (ty, var_name) = Self::type_and_name_from_c(arg);
+
+        let ty =
+            T::from_c(ty, target).unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'"));
+
+        Argument {
+            pos,
+            name: String::from(var_name),
+            ty: *ty,
+            constraint,
+        }
+    }
+
+    fn as_call_param_c(&self) -> String {
+        self.ty.as_call_param_c(&self.name)
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ArgumentList<T: IntrinsicTypeDefinition> {
+    pub args: Vec<Argument<T>>,
+}
+
+impl<T> ArgumentList<T>
+where
+    T: IntrinsicTypeDefinition,
+{
+    /// Converts the argument list into the call parameters for a C function call.
+    /// e.g. this would generate something like `a, &b, c`
+    pub fn as_call_param_c(&self) -> String {
+        self.iter()
+            .map(|arg| arg.as_call_param_c())
+            .collect::<Vec<String>>()
+            .join(", ")
+    }
+
+    /// Converts the argument list into the call parameters for a Rust function.
+    /// e.g. this would generate something like `a, b, c`
+    pub fn as_call_param_rust(&self) -> String {
+        self.iter()
+            .filter(|a| !a.has_constraint())
+            .map(|arg| arg.name.clone())
+            .collect::<Vec<String>>()
+            .join(", ")
+    }
+
+    pub fn as_constraint_parameters_rust(&self) -> String {
+        self.iter()
+            .filter(|a| a.has_constraint())
+            .map(|arg| arg.name.clone())
+            .collect::<Vec<String>>()
+            .join(", ")
+    }
+
+    /// Creates a line for each argument that initializes an array for C from which `loads` argument
+    /// values can be loaded  as a sliding window.
+    /// e.g `const int32x2_t a_vals = {0x3effffff, 0x3effffff, 0x3f7fffff}`, if loads=2.
+    pub fn gen_arglists_c(&self, indentation: Indentation, loads: u32) -> String {
+        self.iter()
+            .filter(|&arg| !arg.has_constraint())
+            .map(|arg| {
+                format!(
+                    "{indentation}const {ty} {name}_vals[] = {values};",
+                    ty = arg.ty.c_scalar_type(),
+                    name = arg.name,
+                    values = arg.ty.populate_random(indentation, loads, &Language::C)
+                )
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    /// Creates a line for each argument that initializes an array for Rust from which `loads` argument
+    /// values can be loaded as a sliding window, e.g `const A_VALS: [u32; 20]  = [...];`
+    pub fn gen_arglists_rust(&self, indentation: Indentation, loads: u32) -> String {
+        self.iter()
+            .filter(|&arg| !arg.has_constraint())
+            .map(|arg| {
+                format!(
+                    "{indentation}{bind} {name}: [{ty}; {load_size}] = {values};",
+                    bind = arg.rust_vals_array_binding(),
+                    name = arg.rust_vals_array_name(),
+                    ty = arg.ty.rust_scalar_type(),
+                    load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1,
+                    values = arg.ty.populate_random(indentation, loads, &Language::Rust)
+                )
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    /// Creates a line for each argument that initializes the argument from an array `[arg]_vals` at
+    /// an offset `i` using a load intrinsic, in C.
+    /// e.g `uint8x8_t a = vld1_u8(&a_vals[i]);`
+    ///
+    /// ARM-specific
+    pub fn load_values_c(&self, indentation: Indentation) -> String {
+        self.iter()
+            .filter(|&arg| !arg.has_constraint())
+            .map(|arg| {
+                format!(
+                    "{indentation}{ty} {name} = cast<{ty}>({load}(&{name}_vals[i]));\n",
+                    ty = arg.to_c_type(),
+                    name = arg.name,
+                    load = if arg.is_simd() {
+                        arg.ty.get_load_function(Language::C)
+                    } else {
+                        "*".to_string()
+                    }
+                )
+            })
+            .collect()
+    }
+
+    /// Creates a line for each argument that initializes the argument from array `[ARG]_VALS` at
+    /// an offset `i` using a load intrinsic, in Rust.
+    /// e.g `let a = vld1_u8(A_VALS.as_ptr().offset(i));`
+    pub fn load_values_rust(&self, indentation: Indentation) -> String {
+        self.iter()
+            .filter(|&arg| !arg.has_constraint())
+            .map(|arg| {
+                format!(
+                    "{indentation}let {name} = {load}({vals_name}.as_ptr().offset(i));\n",
+                    name = arg.name,
+                    vals_name = arg.rust_vals_array_name(),
+                    load = if arg.is_simd() {
+                        arg.ty.get_load_function(Language::Rust)
+                    } else {
+                        "*".to_string()
+                    },
+                )
+            })
+            .collect()
+    }
+
+    pub fn iter(&self) -> std::slice::Iter<'_, Argument<T>> {
+        self.args.iter()
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/cli.rs b/library/stdarch/crates/intrinsic-test/src/common/cli.rs
new file mode 100644
index 00000000000..1d572723008
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/cli.rs
@@ -0,0 +1,113 @@
+use itertools::Itertools;
+use std::path::PathBuf;
+
+#[derive(Debug, PartialEq)]
+pub enum Language {
+    Rust,
+    C,
+}
+
+pub enum FailureReason {
+    RunC(String),
+    RunRust(String),
+    Difference(String, String, String),
+}
+
+/// Intrinsic test tool
+#[derive(clap::Parser)]
+#[command(
+    name = "Intrinsic test tool",
+    about = "Generates Rust and C programs for intrinsics and compares the output"
+)]
+pub struct Cli {
+    /// The input file containing the intrinsics
+    pub input: PathBuf,
+
+    /// The rust toolchain to use for building the rust code
+    #[arg(long)]
+    pub toolchain: Option<String>,
+
+    /// The C++ compiler to use for compiling the c++ code
+    #[arg(long, default_value_t = String::from("clang++"))]
+    pub cppcompiler: String,
+
+    /// Run the C programs under emulation with this command
+    #[arg(long)]
+    pub runner: Option<String>,
+
+    /// Filename for a list of intrinsics to skip (one per line)
+    #[arg(long)]
+    pub skip: Option<PathBuf>,
+
+    /// Regenerate test programs, but don't build or run them
+    #[arg(long)]
+    pub generate_only: bool,
+
+    /// Pass a target the test suite
+    #[arg(long, default_value_t = String::from("armv7-unknown-linux-gnueabihf"))]
+    pub target: String,
+
+    /// Set the linker
+    #[arg(long)]
+    pub linker: Option<String>,
+
+    /// Set the sysroot for the C++ compiler
+    #[arg(long)]
+    pub cxx_toolchain_dir: Option<String>,
+}
+
+pub struct ProcessedCli {
+    pub filename: PathBuf,
+    pub toolchain: Option<String>,
+    pub cpp_compiler: Option<String>,
+    pub c_runner: String,
+    pub target: String,
+    pub linker: Option<String>,
+    pub cxx_toolchain_dir: Option<String>,
+    pub skip: Vec<String>,
+}
+
+impl ProcessedCli {
+    pub fn new(cli_options: Cli) -> Self {
+        let filename = cli_options.input;
+        let c_runner = cli_options.runner.unwrap_or_default();
+        let target = cli_options.target;
+        let linker = cli_options.linker;
+        let cxx_toolchain_dir = cli_options.cxx_toolchain_dir;
+
+        let skip = if let Some(filename) = cli_options.skip {
+            let data = std::fs::read_to_string(&filename).expect("Failed to open file");
+            data.lines()
+                .map(str::trim)
+                .filter(|s| !s.contains('#'))
+                .map(String::from)
+                .collect_vec()
+        } else {
+            Default::default()
+        };
+
+        let (toolchain, cpp_compiler) = if cli_options.generate_only {
+            (None, None)
+        } else {
+            (
+                Some(
+                    cli_options
+                        .toolchain
+                        .map_or_else(String::new, |t| format!("+{t}")),
+                ),
+                Some(cli_options.cppcompiler),
+            )
+        };
+
+        Self {
+            toolchain,
+            cpp_compiler,
+            c_runner,
+            target,
+            linker,
+            cxx_toolchain_dir,
+            skip,
+            filename,
+        }
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/compare.rs b/library/stdarch/crates/intrinsic-test/src/common/compare.rs
new file mode 100644
index 00000000000..815ccf89fc6
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/compare.rs
@@ -0,0 +1,90 @@
+use super::cli::FailureReason;
+use rayon::prelude::*;
+use std::process::Command;
+
+pub fn compare_outputs(
+    intrinsic_name_list: &Vec<String>,
+    toolchain: &str,
+    runner: &str,
+    target: &str,
+) -> bool {
+    let intrinsics = intrinsic_name_list
+        .par_iter()
+        .filter_map(|intrinsic_name| {
+            let c = Command::new("sh")
+                .arg("-c")
+                .arg(format!("{runner} ./c_programs/{intrinsic_name}"))
+                .output();
+
+            let rust = Command::new("sh")
+                .current_dir("rust_programs")
+                .arg("-c")
+                .arg(format!(
+                    "cargo {toolchain} run --target {target} --bin {intrinsic_name} --release",
+                ))
+                .env("RUSTFLAGS", "-Cdebuginfo=0")
+                .output();
+
+            let (c, rust) = match (c, rust) {
+                (Ok(c), Ok(rust)) => (c, rust),
+                a => panic!("{a:#?}"),
+            };
+
+            if !c.status.success() {
+                error!(
+                    "Failed to run C program for intrinsic {intrinsic_name}\nstdout: {stdout}\nstderr: {stderr}",
+                    stdout = std::str::from_utf8(&c.stdout).unwrap_or(""),
+                    stderr = std::str::from_utf8(&c.stderr).unwrap_or(""),
+                );
+                return Some(FailureReason::RunC(intrinsic_name.clone()));
+            }
+
+            if !rust.status.success() {
+                error!(
+                    "Failed to run Rust program for intrinsic {intrinsic_name}\nstdout: {stdout}\nstderr: {stderr}",
+                    stdout = std::str::from_utf8(&rust.stdout).unwrap_or(""),
+                    stderr = std::str::from_utf8(&rust.stderr).unwrap_or(""),
+                );
+                return Some(FailureReason::RunRust(intrinsic_name.clone()));
+            }
+
+            info!("Comparing intrinsic: {}", intrinsic_name);
+
+            let c = std::str::from_utf8(&c.stdout)
+                .unwrap()
+                .to_lowercase()
+                .replace("-nan", "nan");
+            let rust = std::str::from_utf8(&rust.stdout)
+                .unwrap()
+                .to_lowercase()
+                .replace("-nan", "nan");
+
+            if c == rust {
+                None
+            } else {
+                Some(FailureReason::Difference(intrinsic_name.clone(), c, rust))
+            }
+        })
+        .collect::<Vec<_>>();
+
+    intrinsics.iter().for_each(|reason| match reason {
+        FailureReason::Difference(intrinsic, c, rust) => {
+            println!("Difference for intrinsic: {intrinsic}");
+            let diff = diff::lines(c, rust);
+            diff.iter().for_each(|diff| match diff {
+                diff::Result::Left(c) => println!("C: {c}"),
+                diff::Result::Right(rust) => println!("Rust: {rust}"),
+                diff::Result::Both(_, _) => (),
+            });
+            println!("****************************************************************");
+        }
+        FailureReason::RunC(intrinsic) => {
+            println!("Failed to run C program for intrinsic {intrinsic}")
+        }
+        FailureReason::RunRust(intrinsic) => {
+            println!("Failed to run rust program for intrinsic {intrinsic}")
+        }
+    });
+    println!("{} differences found", intrinsics.len());
+    intrinsics.is_empty()
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/compile_c.rs b/library/stdarch/crates/intrinsic-test/src/common/compile_c.rs
new file mode 100644
index 00000000000..aebb7b111e2
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/compile_c.rs
@@ -0,0 +1,154 @@
+#[derive(Clone)]
+pub struct CompilationCommandBuilder {
+    compiler: String,
+    target: Option<String>,
+    cxx_toolchain_dir: Option<String>,
+    arch_flags: Vec<String>,
+    optimization: String,
+    include_paths: Vec<String>,
+    project_root: Option<String>,
+    output: String,
+    input: String,
+    linker: Option<String>,
+    extra_flags: Vec<String>,
+}
+
+impl CompilationCommandBuilder {
+    pub fn new() -> Self {
+        Self {
+            compiler: String::new(),
+            target: None,
+            cxx_toolchain_dir: None,
+            arch_flags: Vec::new(),
+            optimization: "2".to_string(),
+            include_paths: Vec::new(),
+            project_root: None,
+            output: String::new(),
+            input: String::new(),
+            linker: None,
+            extra_flags: Vec::new(),
+        }
+    }
+
+    pub fn set_compiler(mut self, compiler: &str) -> Self {
+        self.compiler = compiler.to_string();
+        self
+    }
+
+    pub fn set_target(mut self, target: &str) -> Self {
+        self.target = Some(target.to_string());
+        self
+    }
+
+    pub fn set_cxx_toolchain_dir(mut self, path: Option<&str>) -> Self {
+        self.cxx_toolchain_dir = path.map(|p| p.to_string());
+        self
+    }
+
+    pub fn add_arch_flags(mut self, flags: Vec<&str>) -> Self {
+        let mut new_arch_flags = flags.into_iter().map(|v| v.to_string()).collect();
+        self.arch_flags.append(&mut new_arch_flags);
+
+        self
+    }
+
+    pub fn set_opt_level(mut self, optimization: &str) -> Self {
+        self.optimization = optimization.to_string();
+        self
+    }
+
+    /// Sets a list of include paths for compilation.
+    /// The paths that are passed must be relative to the
+    /// "cxx_toolchain_dir" directory path.
+    pub fn set_include_paths(mut self, paths: Vec<&str>) -> Self {
+        self.include_paths = paths.into_iter().map(|path| path.to_string()).collect();
+        self
+    }
+
+    /// Sets the root path of all the generated test files.
+    pub fn set_project_root(mut self, path: &str) -> Self {
+        self.project_root = Some(path.to_string());
+        self
+    }
+
+    /// The name of the output executable, without any suffixes
+    pub fn set_output_name(mut self, path: &str) -> Self {
+        self.output = path.to_string();
+        self
+    }
+
+    /// The name of the input C file, without any suffixes
+    pub fn set_input_name(mut self, path: &str) -> Self {
+        self.input = path.to_string();
+        self
+    }
+
+    pub fn set_linker(mut self, linker: String) -> Self {
+        self.linker = Some(linker);
+        self
+    }
+
+    pub fn add_extra_flags(mut self, flags: Vec<&str>) -> Self {
+        let mut flags: Vec<String> = flags.into_iter().map(|f| f.to_string()).collect();
+        self.extra_flags.append(&mut flags);
+        self
+    }
+
+    pub fn add_extra_flag(self, flag: &str) -> Self {
+        self.add_extra_flags(vec![flag])
+    }
+}
+
+impl CompilationCommandBuilder {
+    pub fn make_string(self) -> String {
+        let arch_flags = self.arch_flags.join("+");
+        let flags = std::env::var("CPPFLAGS").unwrap_or("".into());
+        let project_root = self.project_root.unwrap_or_default();
+        let project_root_str = project_root.as_str();
+        let mut output = self.output.clone();
+        if self.linker.is_some() {
+            output += ".o"
+        };
+        let mut command = format!(
+            "{} {flags} -march={arch_flags} \
+            -O{} \
+            -o {project_root}/{} \
+            {project_root}/{}.cpp",
+            self.compiler, self.optimization, output, self.input,
+        );
+
+        command = command + " " + self.extra_flags.join(" ").as_str();
+
+        if let Some(target) = &self.target {
+            command = command + " --target=" + target;
+        }
+
+        if let (Some(linker), Some(cxx_toolchain_dir)) = (&self.linker, &self.cxx_toolchain_dir) {
+            let include_args = self
+                .include_paths
+                .iter()
+                .map(|path| "--include-directory=".to_string() + cxx_toolchain_dir + path)
+                .collect::<Vec<_>>()
+                .join(" ");
+
+            command = command
+                + " -c "
+                + include_args.as_str()
+                + " && "
+                + linker
+                + " "
+                + project_root_str
+                + "/"
+                + &output
+                + " -o "
+                + project_root_str
+                + "/"
+                + &self.output
+                + " && rm "
+                + project_root_str
+                + "/"
+                + &output;
+        }
+        command
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs
new file mode 100644
index 00000000000..269fb7f90cb
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs
@@ -0,0 +1,17 @@
+use serde::Deserialize;
+use std::ops::Range;
+
+#[derive(Debug, PartialEq, Clone, Deserialize)]
+pub enum Constraint {
+    Equal(i64),
+    Range(Range<i64>),
+}
+
+impl Constraint {
+    pub fn to_range(&self) -> Range<i64> {
+        match self {
+            Constraint::Equal(eq) => *eq..*eq + 1,
+            Constraint::Range(range) => range.clone(),
+        }
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs
new file mode 100644
index 00000000000..84c28cc4bf4
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs
@@ -0,0 +1,198 @@
+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(),
+    )
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs
new file mode 100644
index 00000000000..a2878502ac9
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs
@@ -0,0 +1,243 @@
+use itertools::Itertools;
+use rayon::prelude::*;
+use std::collections::BTreeMap;
+use std::fs::File;
+use std::io::Write;
+use std::process::Command;
+
+use super::argument::Argument;
+use super::indentation::Indentation;
+use super::intrinsic::{IntrinsicDefinition, format_f16_return_value};
+use super::intrinsic_helpers::IntrinsicTypeDefinition;
+
+// The number of times each intrinsic will be called.
+const PASSES: u32 = 20;
+
+pub fn format_rust_main_template(
+    notices: &str,
+    definitions: &str,
+    configurations: &str,
+    arch_definition: &str,
+    arglists: &str,
+    passes: &str,
+) -> String {
+    format!(
+        r#"{notices}#![feature(simd_ffi)]
+#![feature(link_llvm_intrinsics)]
+#![feature(f16)]
+{configurations}
+{definitions}
+
+use core_arch::arch::{arch_definition}::*;
+
+fn main() {{
+{arglists}
+{passes}
+}}
+"#,
+    )
+}
+
+pub fn compile_rust_programs(
+    binaries: Vec<String>,
+    toolchain: Option<&str>,
+    target: &str,
+    linker: Option<&str>,
+) -> bool {
+    let mut cargo = File::create("rust_programs/Cargo.toml").unwrap();
+    cargo
+        .write_all(
+            format!(
+                r#"[package]
+name = "intrinsic-test-programs"
+version = "{version}"
+authors = [{authors}]
+license = "{license}"
+edition = "2018"
+[workspace]
+[dependencies]
+core_arch = {{ path = "../crates/core_arch" }}
+{binaries}"#,
+                version = env!("CARGO_PKG_VERSION"),
+                authors = env!("CARGO_PKG_AUTHORS")
+                    .split(":")
+                    .format_with(", ", |author, fmt| fmt(&format_args!("\"{author}\""))),
+                license = env!("CARGO_PKG_LICENSE"),
+                binaries = binaries
+                    .iter()
+                    .map(|binary| {
+                        format!(
+                            r#"[[bin]]
+name = "{binary}"
+path = "{binary}/main.rs""#,
+                        )
+                    })
+                    .collect::<Vec<_>>()
+                    .join("\n")
+            )
+            .into_bytes()
+            .as_slice(),
+        )
+        .unwrap();
+
+    let toolchain = match toolchain {
+        None => return true,
+        Some(t) => t,
+    };
+
+    /* If there has been a linker explicitly set from the command line then
+     * we want to set it via setting it in the RUSTFLAGS*/
+
+    let cargo_command = format!("cargo {toolchain} build --target {target} --release");
+
+    let mut command = Command::new("sh");
+    command
+        .current_dir("rust_programs")
+        .arg("-c")
+        .arg(cargo_command);
+
+    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");
+
+        command.env("CPPFLAGS", "-fuse-ld=lld");
+    }
+
+    command.env("RUSTFLAGS", rust_flags);
+    let output = command.output();
+
+    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 {
+        error!("Command failed: {:#?}", output);
+        false
+    }
+}
+
+// Creates directory structure and file path mappings
+pub fn setup_rust_file_paths(identifiers: &Vec<String>) -> BTreeMap<&String, String> {
+    identifiers
+        .par_iter()
+        .map(|identifier| {
+            let rust_dir = format!("rust_programs/{identifier}");
+            let _ = std::fs::create_dir_all(&rust_dir);
+            let rust_filename = format!("{rust_dir}/main.rs");
+
+            (identifier, rust_filename)
+        })
+        .collect::<BTreeMap<&String, String>>()
+}
+
+pub fn generate_rust_test_loop<T: IntrinsicTypeDefinition>(
+    intrinsic: &dyn IntrinsicDefinition<T>,
+    indentation: Indentation,
+    additional: &str,
+    passes: u32,
+) -> String {
+    let constraints = intrinsic.arguments().as_constraint_parameters_rust();
+    let constraints = if !constraints.is_empty() {
+        format!("::<{constraints}>")
+    } else {
+        constraints
+    };
+
+    let return_value = format_f16_return_value(intrinsic);
+    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 = intrinsic.arguments().load_values_rust(indentation3),
+        intrinsic_call = intrinsic.name(),
+        const = constraints,
+        args = intrinsic.arguments().as_call_param_rust(),
+    )
+}
+
+pub fn generate_rust_constraint_blocks<T: IntrinsicTypeDefinition>(
+    intrinsic: &dyn IntrinsicDefinition<T>,
+    indentation: Indentation,
+    constraints: &[&Argument<T>],
+    name: String,
+) -> 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}const {name}: {ty} = {val};\n\
+                        {pass}\n\
+                    {indentation}}}",
+                    name = current.name,
+                    ty = current.ty.rust_type(),
+                    val = i,
+                    pass = generate_rust_constraint_blocks(
+                        intrinsic,
+                        body_indentation,
+                        constraints,
+                        format!("{name}-{i}")
+                    )
+                )
+            })
+            .join("\n")
+    } else {
+        generate_rust_test_loop(intrinsic, indentation, &name, PASSES)
+    }
+}
+
+// Top-level function to create complete test program
+pub fn create_rust_test_program<T: IntrinsicTypeDefinition>(
+    intrinsic: &dyn IntrinsicDefinition<T>,
+    target: &str,
+    notice: &str,
+    definitions: &str,
+    cfg: &str,
+) -> String {
+    let arguments = intrinsic.arguments();
+    let constraints = arguments
+        .iter()
+        .filter(|i| i.has_constraint())
+        .collect_vec();
+
+    let indentation = Indentation::default();
+    format_rust_main_template(
+        notice,
+        definitions,
+        cfg,
+        target,
+        intrinsic
+            .arguments()
+            .gen_arglists_rust(indentation.nested(), PASSES)
+            .as_str(),
+        generate_rust_constraint_blocks(
+            intrinsic,
+            indentation.nested(),
+            &constraints,
+            Default::default(),
+        )
+        .as_str(),
+    )
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/indentation.rs b/library/stdarch/crates/intrinsic-test/src/common/indentation.rs
new file mode 100644
index 00000000000..9ee331d7f7a
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/indentation.rs
@@ -0,0 +1,22 @@
+//! Basic code formatting tools.
+//!
+//! We don't need perfect formatting for the generated tests, but simple indentation can make
+//! debugging a lot easier.
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct Indentation(u32);
+
+impl Indentation {
+    pub fn nested(self) -> Self {
+        Self(self.0 + 1)
+    }
+}
+
+impl std::fmt::Display for Indentation {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        for _ in 0..self.0 {
+            write!(f, "    ")?;
+        }
+        Ok(())
+    }
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs
new file mode 100644
index 00000000000..bc46ccfbac4
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs
@@ -0,0 +1,51 @@
+use super::argument::ArgumentList;
+use super::indentation::Indentation;
+use super::intrinsic_helpers::{IntrinsicTypeDefinition, TypeKind};
+
+/// An intrinsic
+#[derive(Debug, PartialEq, Clone)]
+pub struct Intrinsic<T: IntrinsicTypeDefinition> {
+    /// The function name of this intrinsic.
+    pub name: String,
+
+    /// Any arguments for this intrinsic.
+    pub arguments: ArgumentList<T>,
+
+    /// The return type of this intrinsic.
+    pub results: T,
+
+    /// Any architecture-specific tags.
+    pub arch_tags: Vec<String>,
+}
+
+pub trait IntrinsicDefinition<T>
+where
+    T: IntrinsicTypeDefinition,
+{
+    fn arguments(&self) -> ArgumentList<T>;
+
+    fn results(&self) -> T;
+
+    fn name(&self) -> String;
+
+    /// 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.
+    fn print_result_c(&self, _indentation: Indentation, _additional: &str) -> String;
+}
+
+pub fn format_f16_return_value<T: IntrinsicTypeDefinition>(
+    intrinsic: &dyn IntrinsicDefinition<T>,
+) -> String {
+    // 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 intrinsic.results().kind() {
+        TypeKind::Float if intrinsic.results().inner_size() == 16 => "debug_f16(__return_value)",
+        _ => "format_args!(\"{__return_value:.150?}\")",
+    };
+
+    String::from(return_value)
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs
new file mode 100644
index 00000000000..3d200b19461
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs
@@ -0,0 +1,296 @@
+use std::fmt;
+use std::ops::Deref;
+use std::str::FromStr;
+
+use itertools::Itertools as _;
+
+use super::cli::Language;
+use super::indentation::Indentation;
+use super::values::value_for_array;
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum TypeKind {
+    BFloat,
+    Float,
+    Int,
+    UInt,
+    Poly,
+    Void,
+}
+
+impl FromStr for TypeKind {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "bfloat" => Ok(Self::BFloat),
+            "float" => Ok(Self::Float),
+            "int" => Ok(Self::Int),
+            "poly" => Ok(Self::Poly),
+            "uint" | "unsigned" => Ok(Self::UInt),
+            "void" => Ok(Self::Void),
+            _ => Err(format!("Impossible to parse argument kind {s}")),
+        }
+    }
+}
+
+impl fmt::Display for TypeKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                Self::BFloat => "bfloat",
+                Self::Float => "float",
+                Self::Int => "int",
+                Self::UInt => "uint",
+                Self::Poly => "poly",
+                Self::Void => "void",
+            }
+        )
+    }
+}
+
+impl TypeKind {
+    /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t.
+    pub fn c_prefix(&self) -> &str {
+        match self {
+            Self::Float => "float",
+            Self::Int => "int",
+            Self::UInt => "uint",
+            Self::Poly => "poly",
+            _ => unreachable!("Not used: {:#?}", self),
+        }
+    }
+
+    /// Gets the rust prefix for the type kind i.e. i, u, f.
+    pub fn rust_prefix(&self) -> &str {
+        match self {
+            Self::Float => "f",
+            Self::Int => "i",
+            Self::UInt => "u",
+            Self::Poly => "u",
+            _ => unreachable!("Unused type kind: {:#?}", self),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct IntrinsicType {
+    pub constant: bool,
+
+    /// whether this object is a const pointer
+    pub ptr_constant: bool,
+
+    pub ptr: bool,
+
+    pub kind: TypeKind,
+    /// The bit length of this type (e.g. 32 for u32).
+    pub bit_len: Option<u32>,
+
+    /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None`
+    /// means this is not a simd type. A `None` can be assumed to be 1,
+    /// although in some places a distinction is needed between `u64` and
+    /// `uint64x1_t` this signals that.
+    pub simd_len: Option<u32>,
+
+    /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t).
+    /// A value of `None` represents a type that does not contain any
+    /// rows encoded in the type (e.g. uint8x8_t).
+    /// A value of `None` can be assumed to be 1 though.
+    pub vec_len: Option<u32>,
+
+    pub target: String,
+}
+
+impl IntrinsicType {
+    pub fn kind(&self) -> TypeKind {
+        self.kind
+    }
+
+    pub fn inner_size(&self) -> u32 {
+        if let Some(bl) = self.bit_len {
+            bl
+        } else {
+            unreachable!("")
+        }
+    }
+
+    pub fn num_lanes(&self) -> u32 {
+        self.simd_len.unwrap_or(1)
+    }
+
+    pub fn num_vectors(&self) -> u32 {
+        self.vec_len.unwrap_or(1)
+    }
+
+    pub fn is_simd(&self) -> bool {
+        self.simd_len.is_some() || self.vec_len.is_some()
+    }
+
+    pub fn is_ptr(&self) -> bool {
+        self.ptr
+    }
+
+    pub fn c_scalar_type(&self) -> String {
+        format!(
+            "{prefix}{bits}_t",
+            prefix = self.kind().c_prefix(),
+            bits = self.inner_size()
+        )
+    }
+
+    pub fn rust_scalar_type(&self) -> String {
+        format!(
+            "{prefix}{bits}",
+            prefix = self.kind().rust_prefix(),
+            bits = self.inner_size()
+        )
+    }
+
+    pub fn c_promotion(&self) -> &str {
+        match *self {
+            IntrinsicType {
+                kind,
+                bit_len: Some(8),
+                ..
+            } => match kind {
+                TypeKind::Int => "(int)",
+                TypeKind::UInt => "(unsigned int)",
+                TypeKind::Poly => "(unsigned int)(uint8_t)",
+                _ => "",
+            },
+            IntrinsicType {
+                kind: TypeKind::Poly,
+                bit_len: Some(bit_len),
+                ..
+            } => match bit_len {
+                8 => unreachable!("handled above"),
+                16 => "(uint16_t)",
+                32 => "(uint32_t)",
+                64 => "(uint64_t)",
+                128 => "",
+                _ => panic!("invalid bit_len"),
+            },
+            _ => "",
+        }
+    }
+
+    pub fn populate_random(
+        &self,
+        indentation: Indentation,
+        loads: u32,
+        language: &Language,
+    ) -> String {
+        match self {
+            IntrinsicType {
+                bit_len: Some(bit_len @ (8 | 16 | 32 | 64)),
+                kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly),
+                simd_len,
+                vec_len,
+                ..
+            } => {
+                let (prefix, suffix) = match language {
+                    Language::Rust => ("[", "]"),
+                    Language::C => ("{", "}"),
+                };
+                let body_indentation = indentation.nested();
+                format!(
+                    "{prefix}\n{body}\n{indentation}{suffix}",
+                    body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
+                        .format_with(",\n", |i, fmt| {
+                            let src = value_for_array(*bit_len, i);
+                            assert!(src == 0 || src.ilog2() < *bit_len);
+                            if *kind == TypeKind::Int && (src >> (*bit_len - 1)) != 0 {
+                                // `src` is a two's complement representation of a negative value.
+                                let mask = !0u64 >> (64 - *bit_len);
+                                let ones_compl = src ^ mask;
+                                let twos_compl = ones_compl + 1;
+                                if (twos_compl == src) && (language == &Language::C) {
+                                    // `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid
+                                    // undefined literal overflow behaviour.
+                                    fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1"))
+                                } else {
+                                    fmt(&format_args!("{body_indentation}-{twos_compl:#x}"))
+                                }
+                            } else {
+                                fmt(&format_args!("{body_indentation}{src:#x}"))
+                            }
+                        })
+                )
+            }
+            IntrinsicType {
+                kind: TypeKind::Float,
+                bit_len: Some(bit_len @ (16 | 32 | 64)),
+                simd_len,
+                vec_len,
+                ..
+            } => {
+                let (prefix, cast_prefix, cast_suffix, suffix) = match (language, bit_len) {
+                    (&Language::Rust, 16) => ("[", "f16::from_bits(", ")", "]"),
+                    (&Language::Rust, 32) => ("[", "f32::from_bits(", ")", "]"),
+                    (&Language::Rust, 64) => ("[", "f64::from_bits(", ")", "]"),
+                    (&Language::C, 16) => ("{", "cast<float16_t, uint16_t>(", ")", "}"),
+                    (&Language::C, 32) => ("{", "cast<float, uint32_t>(", ")", "}"),
+                    (&Language::C, 64) => ("{", "cast<double, uint64_t>(", ")", "}"),
+                    _ => unreachable!(),
+                };
+                format!(
+                    "{prefix}\n{body}\n{indentation}{suffix}",
+                    body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
+                        .format_with(",\n", |i, fmt| fmt(&format_args!(
+                            "{indentation}{cast_prefix}{src:#x}{cast_suffix}",
+                            indentation = indentation.nested(),
+                            src = value_for_array(*bit_len, i)
+                        )))
+                )
+            }
+            _ => unimplemented!("populate random: {:#?}", self),
+        }
+    }
+
+    pub fn is_rust_vals_array_const(&self) -> bool {
+        match self {
+            // Floats have to be loaded at runtime for stable NaN conversion.
+            IntrinsicType {
+                kind: TypeKind::Float,
+                ..
+            } => false,
+            IntrinsicType {
+                kind: TypeKind::Int | TypeKind::UInt | TypeKind::Poly,
+                ..
+            } => true,
+            _ => unimplemented!(),
+        }
+    }
+
+    pub fn as_call_param_c(&self, name: &String) -> String {
+        if self.ptr {
+            format!("&{name}")
+        } else {
+            name.clone()
+        }
+    }
+}
+
+pub trait IntrinsicTypeDefinition: Deref<Target = IntrinsicType> {
+    /// Determines the load function for this type.
+    /// can be implemented in an `impl` block
+    fn get_load_function(&self, _language: Language) -> String;
+
+    /// can be implemented in an `impl` block
+    fn get_lane_function(&self) -> String;
+
+    /// can be implemented in an `impl` block
+    fn from_c(_s: &str, _target: &str) -> Result<Box<Self>, String>;
+
+    /// Gets a string containing the typename for this type in C format.
+    /// can be directly defined in `impl` blocks
+    fn c_type(&self) -> String;
+
+    /// can be directly defined in `impl` blocks
+    fn c_single_vector_type(&self) -> String;
+
+    /// can be defined in `impl` blocks
+    fn rust_type(&self) -> String;
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs
new file mode 100644
index 00000000000..5d51d3460ec
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs
@@ -0,0 +1,25 @@
+use cli::ProcessedCli;
+
+pub mod argument;
+pub mod cli;
+pub mod compare;
+pub mod compile_c;
+pub mod constraint;
+pub mod gen_c;
+pub mod gen_rust;
+pub mod indentation;
+pub mod intrinsic;
+pub mod intrinsic_helpers;
+pub mod values;
+pub mod write_file;
+
+/// Architectures must support this trait
+/// to be successfully tested.
+pub trait SupportedArchitectureTest {
+    fn create(cli_options: ProcessedCli) -> Box<Self>
+    where
+        Self: Sized;
+    fn build_c_file(&self) -> bool;
+    fn build_rust_file(&self) -> bool;
+    fn compare_outputs(&self) -> bool;
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs
new file mode 100644
index 00000000000..1b614a742ef
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs
@@ -0,0 +1,120 @@
+/// Get a single value for an argument values array in a determistic way.
+/// * `bits`: The number of bits for the type, only 8, 16, 32, 64 are valid values
+/// * `index`: The position in the array we are generating for
+pub fn value_for_array(bits: u32, index: u32) -> u64 {
+    let index = index as usize;
+    match bits {
+        8 => VALUES_8[index % VALUES_8.len()].into(),
+        16 => VALUES_16[index % VALUES_16.len()].into(),
+        32 => VALUES_32[index % VALUES_32.len()].into(),
+        64 => VALUES_64[index % VALUES_64.len()],
+        _ => unimplemented!("value_for_array(bits: {bits}, ..)"),
+    }
+}
+
+pub const VALUES_8: &[u8] = &[
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0xf0, 0x80, 0x3b, 0xff,
+];
+
+pub const VALUES_16: &[u16] = &[
+    0x0000, // 0.0
+    0x0400, // The smallest normal value.
+    0x37ff, // The value just below 0.5.
+    0x3800, // 0.5
+    0x3801, // The value just above 0.5.
+    0x3bff, // The value just below 1.0.
+    0x3c00, // 1.0
+    0x3c01, // The value just above 1.0.
+    0x3e00, // 1.5
+    0x4900, // 10
+    0x7bff, // The largest finite value.
+    0x7c00, // Infinity.
+    // NaNs.
+    //  - Quiet NaNs
+    0x7f23, 0x7e00, //  - Signalling NaNs
+    0x7d23, 0x7c01, // Subnormals.
+    //  - A recognisable bit pattern.
+    0x0012, //  - The largest subnormal value.
+    0x03ff, //  - The smallest subnormal value.
+    0x0001, // The same values again, but negated.
+    0x8000, 0x8400, 0xb7ff, 0xb800, 0xb801, 0xbbff, 0xbc00, 0xbc01, 0xbe00, 0xc900, 0xfbff, 0xfc00,
+    0xff23, 0xfe00, 0xfd23, 0xfc01, 0x8012, 0x83ff, 0x8001,
+];
+
+pub const VALUES_32: &[u32] = &[
+    // Simple values.
+    0x00000000, // 0.0
+    0x00800000, // The smallest normal value.
+    0x3effffff, // The value just below 0.5.
+    0x3f000000, // 0.5
+    0x3f000001, // The value just above 0.5.
+    0x3f7fffff, // The value just below 1.0.
+    0x3f800000, // 1.0
+    0x3f800001, // The value just above 1.0.
+    0x3fc00000, // 1.5
+    0x41200000, // 10
+    0x7f8fffff, // The largest finite value.
+    0x7f800000, // Infinity.
+    // NaNs.
+    //  - Quiet NaNs
+    0x7fd23456, 0x7fc00000, //  - Signalling NaNs
+    0x7f923456, 0x7f800001, // Subnormals.
+    //  - A recognisable bit pattern.
+    0x00123456, //  - The largest subnormal value.
+    0x007fffff, //  - The smallest subnormal value.
+    0x00000001, // The same values again, but negated.
+    0x80000000, 0x80800000, 0xbeffffff, 0xbf000000, 0xbf000001, 0xbf7fffff, 0xbf800000, 0xbf800001,
+    0xbfc00000, 0xc1200000, 0xff8fffff, 0xff800000, 0xffd23456, 0xffc00000, 0xff923456, 0xff800001,
+    0x80123456, 0x807fffff, 0x80000001,
+];
+
+pub const VALUES_64: &[u64] = &[
+    // Simple values.
+    0x0000000000000000, // 0.0
+    0x0010000000000000, // The smallest normal value.
+    0x3fdfffffffffffff, // The value just below 0.5.
+    0x3fe0000000000000, // 0.5
+    0x3fe0000000000001, // The value just above 0.5.
+    0x3fefffffffffffff, // The value just below 1.0.
+    0x3ff0000000000000, // 1.0
+    0x3ff0000000000001, // The value just above 1.0.
+    0x3ff8000000000000, // 1.5
+    0x4024000000000000, // 10
+    0x7fefffffffffffff, // The largest finite value.
+    0x7ff0000000000000, // Infinity.
+    // NaNs.
+    //  - Quiet NaNs
+    0x7ff923456789abcd,
+    0x7ff8000000000000,
+    //  - Signalling NaNs
+    0x7ff123456789abcd,
+    0x7ff0000000000000,
+    // Subnormals.
+    //  - A recognisable bit pattern.
+    0x000123456789abcd,
+    //  - The largest subnormal value.
+    0x000fffffffffffff,
+    //  - The smallest subnormal value.
+    0x0000000000000001,
+    // The same values again, but negated.
+    0x8000000000000000,
+    0x8010000000000000,
+    0xbfdfffffffffffff,
+    0xbfe0000000000000,
+    0xbfe0000000000001,
+    0xbfefffffffffffff,
+    0xbff0000000000000,
+    0xbff0000000000001,
+    0xbff8000000000000,
+    0xc024000000000000,
+    0xffefffffffffffff,
+    0xfff0000000000000,
+    0xfff923456789abcd,
+    0xfff8000000000000,
+    0xfff123456789abcd,
+    0xfff0000000000000,
+    0x800123456789abcd,
+    0x800fffffffffffff,
+    0x8000000000000001,
+];
diff --git a/library/stdarch/crates/intrinsic-test/src/common/write_file.rs b/library/stdarch/crates/intrinsic-test/src/common/write_file.rs
new file mode 100644
index 00000000000..0ba3e829a6b
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/common/write_file.rs
@@ -0,0 +1,66 @@
+use super::gen_c::create_c_test_program;
+use super::gen_c::setup_c_file_paths;
+use super::gen_rust::{create_rust_test_program, setup_rust_file_paths};
+use super::intrinsic::IntrinsicDefinition;
+use super::intrinsic_helpers::IntrinsicTypeDefinition;
+use std::fs::File;
+use std::io::Write;
+
+pub fn write_file(filename: &String, code: String) {
+    let mut file = File::create(filename).unwrap();
+    file.write_all(code.into_bytes().as_slice()).unwrap();
+}
+
+pub fn write_c_testfiles<T: IntrinsicTypeDefinition + Sized>(
+    intrinsics: &Vec<&dyn IntrinsicDefinition<T>>,
+    target: &str,
+    c_target: &str,
+    headers: &[&str],
+    notice: &str,
+    arch_specific_definitions: &[&str],
+) -> Vec<String> {
+    let intrinsics_name_list = intrinsics
+        .iter()
+        .map(|i| i.name().clone())
+        .collect::<Vec<_>>();
+    let filename_mapping = setup_c_file_paths(&intrinsics_name_list);
+
+    intrinsics.iter().for_each(|&i| {
+        let c_code = create_c_test_program(
+            i,
+            headers,
+            target,
+            c_target,
+            notice,
+            arch_specific_definitions,
+        );
+        if let Some(filename) = filename_mapping.get(&i.name()) {
+            write_file(filename, c_code)
+        };
+    });
+
+    intrinsics_name_list
+}
+
+pub fn write_rust_testfiles<T: IntrinsicTypeDefinition>(
+    intrinsics: Vec<&dyn IntrinsicDefinition<T>>,
+    rust_target: &str,
+    notice: &str,
+    definitions: &str,
+    cfg: &str,
+) -> Vec<String> {
+    let intrinsics_name_list = intrinsics
+        .iter()
+        .map(|i| i.name().clone())
+        .collect::<Vec<_>>();
+    let filename_mapping = setup_rust_file_paths(&intrinsics_name_list);
+
+    intrinsics.iter().for_each(|&i| {
+        let rust_code = create_rust_test_program(i, rust_target, notice, definitions, cfg);
+        if let Some(filename) = filename_mapping.get(&i.name()) {
+            write_file(filename, rust_code)
+        }
+    });
+
+    intrinsics_name_list
+}
diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs
new file mode 100644
index 00000000000..054138a0dba
--- /dev/null
+++ b/library/stdarch/crates/intrinsic-test/src/main.rs
@@ -0,0 +1,42 @@
+#[macro_use]
+extern crate log;
+
+mod arm;
+mod common;
+
+use arm::ArmArchitectureTest;
+use common::SupportedArchitectureTest;
+use common::cli::{Cli, ProcessedCli};
+
+fn main() {
+    pretty_env_logger::init();
+    let args: Cli = clap::Parser::parse();
+    let processed_cli_options = ProcessedCli::new(args);
+
+    let test_environment_result: Option<Box<dyn SupportedArchitectureTest>> =
+        match processed_cli_options.target.as_str() {
+            "aarch64-unknown-linux-gnu"
+            | "armv7-unknown-linux-gnueabihf"
+            | "aarch64_be-unknown-linux-gnu" => {
+                Some(ArmArchitectureTest::create(processed_cli_options))
+            }
+
+            _ => None,
+        };
+
+    if test_environment_result.is_none() {
+        std::process::exit(0);
+    }
+
+    let test_environment = test_environment_result.unwrap();
+
+    if !test_environment.build_c_file() {
+        std::process::exit(2);
+    }
+    if !test_environment.build_rust_file() {
+        std::process::exit(3);
+    }
+    if !test_environment.compare_outputs() {
+        std::process::exit(1);
+    }
+}