Skip to content

Adding the x86 part of behavioural testing for std::arch intrinsics #1814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/intrinsic-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ pretty_env_logger = "0.5.0"
rayon = "1.5.0"
diff = "0.1.12"
itertools = "0.14.0"
quick-xml = { version = "0.37.5", features = ["serialize", "overlapped-lists"] }
serde-xml-rs = "0.8.0"
2 changes: 1 addition & 1 deletion crates/intrinsic-test/src/arm/json_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn json_to_intrinsic(
Ok(Intrinsic {
name,
arguments,
results: *results,
results: results,
arch_tags: intr.architectures,
})
}
Expand Down
15 changes: 7 additions & 8 deletions crates/intrinsic-test/src/arm/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
}
}

fn from_c(s: &str, target: &String) -> Result<Box<Self>, String> {
fn from_c(s: &str, target: &String) -> Result<Self, String> {
const CONST_STR: &str = "const";
if let Some(s) = s.strip_suffix('*') {
let (s, constant) = match s.trim().strip_suffix(CONST_STR) {
Expand All @@ -129,9 +129,8 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
let s = s.trim_end();
let temp_return = ArmIntrinsicType::from_c(s, target);
temp_return.and_then(|mut op| {
let edited = op.as_mut();
edited.0.ptr = true;
edited.0.ptr_constant = constant;
op.0.ptr = true;
op.0.ptr_constant = constant;
Ok(op)
})
} else {
Expand Down Expand Up @@ -161,7 +160,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
),
None => None,
};
Ok(Box::new(ArmIntrinsicType(IntrinsicType {
Ok(ArmIntrinsicType(IntrinsicType {
ptr: false,
ptr_constant: false,
constant,
Expand All @@ -170,14 +169,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
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 {
Ok(ArmIntrinsicType(IntrinsicType {
ptr: false,
ptr_constant: false,
constant,
Expand All @@ -186,7 +185,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
simd_len: None,
vec_len: None,
target: target.to_string(),
})))
}))
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion crates/intrinsic-test/src/common/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ impl<T> Argument<T>
where
T: IntrinsicTypeDefinition,
{
pub fn new(pos: usize, name: String, ty: T, constraint: Option<Constraint>) -> Self {
Argument {
pos,
name,
ty,
constraint,
}
}

pub fn to_c_type(&self) -> String {
self.ty.c_type()
}
Expand Down Expand Up @@ -76,7 +85,7 @@ where
Argument {
pos,
name: String::from(var_name),
ty: *ty,
ty: ty,
constraint,
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/intrinsic-test/src/common/intrinsic_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ pub trait IntrinsicTypeDefinition: Deref<Target = IntrinsicType> {
fn get_lane_function(&self) -> String;

/// can be implemented in an `impl` block
fn from_c(_s: &str, _target: &String) -> Result<Box<Self>, String>;
fn from_c(_s: &str, _target: &String) -> Result<Self, String>
where
Self: Sized;

/// Gets a string containing the typename for this type in C format.
/// can be directly defined in `impl` blocks
Expand Down
4 changes: 4 additions & 0 deletions crates/intrinsic-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ extern crate log;

mod arm;
mod common;
mod x86;

use arm::ArmArchitectureTest;
use common::SupportedArchitectureTest;
use common::cli::{Cli, ProcessedCli};
use x86::X86ArchitectureTest;

fn main() {
pretty_env_logger::init();
Expand All @@ -21,6 +23,8 @@ fn main() {
Some(ArmArchitectureTest::create(processed_cli_options))
}

"x86_64-unknown-linux-gnu" => Some(X86ArchitectureTest::create(processed_cli_options)),

_ => None,
};

Expand Down
37 changes: 37 additions & 0 deletions crates/intrinsic-test/src/x86/intrinsic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::common::argument::ArgumentList;
use crate::common::indentation::Indentation;
use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
use crate::common::intrinsic_helpers::IntrinsicType;
use std::ops::Deref;

#[derive(Debug, Clone, PartialEq)]
pub struct X86IntrinsicType(pub IntrinsicType);

impl Deref for X86IntrinsicType {
type Target = IntrinsicType;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl IntrinsicDefinition<X86IntrinsicType> for Intrinsic<X86IntrinsicType> {
fn arguments(&self) -> ArgumentList<X86IntrinsicType> {
self.arguments.clone()
}

fn results(&self) -> X86IntrinsicType {
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 {
todo!("print_result_c in Intrinsic<X86IntrinsicType> needs to be implemented!");
}
}
38 changes: 38 additions & 0 deletions crates/intrinsic-test/src/x86/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod intrinsic;
mod types;
mod xml_parser;

use crate::common::SupportedArchitectureTest;
use crate::common::cli::ProcessedCli;
use crate::common::intrinsic::Intrinsic;
use intrinsic::X86IntrinsicType;
use xml_parser::get_xml_intrinsics;

pub struct X86ArchitectureTest {
intrinsics: Vec<Intrinsic<X86IntrinsicType>>,
cli_options: ProcessedCli,
}

impl SupportedArchitectureTest for X86ArchitectureTest {
fn create(cli_options: ProcessedCli) -> Box<Self> {
let intrinsics = get_xml_intrinsics(&cli_options.filename, &cli_options.target)
.expect("Error parsing input file");

Box::new(Self {
intrinsics: intrinsics,
cli_options: cli_options,
})
}

fn build_c_file(&self) -> bool {
todo!("build_c_file in X86ArchitectureTest is not implemented")
}

fn build_rust_file(&self) -> bool {
todo!("build_rust_file in X86ArchitectureTest is not implemented")
}

fn compare_outputs(&self) -> bool {
todo!("compare_outputs in X86ArchitectureTest is not implemented")
}
}
32 changes: 32 additions & 0 deletions crates/intrinsic-test/src/x86/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::intrinsic::X86IntrinsicType;
use crate::common::cli::Language;
use crate::common::intrinsic_helpers::IntrinsicTypeDefinition;

impl IntrinsicTypeDefinition for X86IntrinsicType {
/// Gets a string containing the typename for this type in C format.
fn c_type(&self) -> String {
todo!("c_type for X86IntrinsicType needs to be implemented!");
}

fn c_single_vector_type(&self) -> String {
todo!("c_single_vector_type for X86IntrinsicType needs to be implemented!");
}

fn rust_type(&self) -> String {
todo!("rust_type for X86IntrinsicType needs to be implemented!");
}

/// Determines the load function for this type.
fn get_load_function(&self, language: Language) -> String {
todo!("get_load_function for X86IntrinsicType needs to be implemented!");
}

/// Determines the get lane function for this type.
fn get_lane_function(&self) -> String {
todo!("get_lane_function for X86IntrinsicType needs to be implemented!");
}

fn from_c(s: &str, target: &String) -> Result<Self, String> {
todo!("from_c for X86IntrinsicType needs to be implemented!");
}
}
136 changes: 136 additions & 0 deletions crates/intrinsic-test/src/x86/xml_parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::common::argument::{Argument, ArgumentList};
use crate::common::intrinsic::Intrinsic;
use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition};

use serde::{Deserialize, Deserializer};
use std::path::Path;

use super::intrinsic::X86IntrinsicType;

// Custom deserializer function to convert "TRUE"/"FALSE" strings to boolean
fn string_to_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"TRUE" => Ok(true),
"FALSE" => Ok(false),
_ => Ok(false), // Default to false for any other value
}
}

// Custom deserializer function to convert strings to u16
fn string_to_u16<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
return Ok(s.as_str().parse::<u16>().unwrap_or(0u16));
}

#[derive(Deserialize)]
struct Data {
#[serde(rename = "intrinsic", default)]
intrinsics: Vec<XMLIntrinsic>,
}

#[derive(Deserialize)]
struct XMLIntrinsic {
#[serde(rename = "return")]
return_data: Return,
#[serde(rename = "@name")]
name: String,
#[serde(rename = "@tech")]
tech: String,
#[serde(rename = "CPUID", default)]
cpuid: Vec<String>,
#[serde(rename = "parameter", default)]
parameters: Vec<Parameter>,
#[serde(rename = "@sequence", default, deserialize_with = "string_to_bool")]
generates_sequence: bool,
#[serde(default)]
instruction: Vec<Instruction>,
}

#[derive(Deserialize)]
struct Parameter {
#[serde(rename = "@type")]
type_data: String,
#[serde(rename = "@etype", default)]
etype: String,
#[serde(rename = "@memwidth", default, deserialize_with = "string_to_u16")]
memwidth: u16,
#[serde(rename = "@varname", default)]
var_name: String,
}

#[derive(Deserialize)]
struct Return {
#[serde(rename = "@type", default)]
type_data: String,
}

#[derive(Deserialize, Debug)]
struct Instruction {
#[serde(rename = "@name")]
name: String,
}

pub fn get_xml_intrinsics(
filename: &Path,
target: &String,
) -> Result<Vec<Intrinsic<X86IntrinsicType>>, Box<dyn std::error::Error>> {
let file = std::fs::File::open(filename)?;
let reader = std::io::BufReader::new(file);
let data: Data =
quick_xml::de::from_reader(reader).expect("failed to deserialize the source XML file");

// println!("{} intrinsics found", data.intrinsics.len());
let parsed_intrinsics: Vec<Intrinsic<X86IntrinsicType>> = data
.intrinsics
.into_iter()
.filter_map(|intr| {
Some(xml_to_intrinsic(intr, target).expect("Couldn't parse XML properly!"))
})
.collect();

Ok(parsed_intrinsics)
}

fn xml_to_intrinsic(
intr: XMLIntrinsic,
target: &String,
) -> Result<Intrinsic<X86IntrinsicType>, Box<dyn std::error::Error>> {
let name = intr.name;
let results = X86IntrinsicType::from_c(&intr.return_data.type_data, target)?;

let args: Vec<_> = intr
.parameters
.into_iter()
.enumerate()
.map(|(i, param)| {
let constraint = None;
let ty = X86IntrinsicType::from_c(param.type_data.as_str(), target)
.unwrap_or_else(|_| panic!("Failed to parse argument '{i}'"));

let mut arg = Argument::<X86IntrinsicType>::new(i, param.var_name, ty, constraint);
let IntrinsicType {
ref mut constant, ..
} = arg.ty.0;
if param.etype == "IMM" {
*constant = true
}
arg
})
.collect();

let arguments = ArgumentList::<X86IntrinsicType> { args };

Ok(Intrinsic {
name,
arguments,
results: results,
arch_tags: intr.cpuid,
})
}