mirror of
https://github.com/Noratrieb/intringen.git
synced 2026-01-14 13:55:02 +01:00
generate _mm_packus_epi16
This commit is contained in:
commit
429d9b826c
13 changed files with 855 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use nix
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
intrinsics.xml
|
||||||
189
Cargo.lock
generated
Normal file
189
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "beef"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eyre"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
|
||||||
|
dependencies = [
|
||||||
|
"indenter",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generate"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"eyre",
|
||||||
|
"heck",
|
||||||
|
"logos",
|
||||||
|
"strong-xml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indenter"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "intringen"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jetscii"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
|
||||||
|
dependencies = [
|
||||||
|
"logos-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-codegen"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
|
||||||
|
dependencies = [
|
||||||
|
"beef",
|
||||||
|
"fnv",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex-syntax",
|
||||||
|
"syn 2.0.46",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-derive"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
|
||||||
|
dependencies = [
|
||||||
|
"logos-codegen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.74"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strong-xml"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d19fb3a618e2f1039e32317c9f525e6d45c55af704ec7c429aa74412419bebf"
|
||||||
|
dependencies = [
|
||||||
|
"jetscii",
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
"strong-xml-derive",
|
||||||
|
"xmlparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strong-xml-derive"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92c781f499321613b112be5d9338189ef1ed19689a01edd23d923ea57ad5c7e1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlparser"
|
||||||
|
version = "0.13.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
|
||||||
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[workspace]
|
||||||
|
members = ["crates/generate", "crates/intringen"]
|
||||||
|
resolver = "2"
|
||||||
12
crates/generate/Cargo.toml
Normal file
12
crates/generate/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "generate"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
eyre = "0.6.11"
|
||||||
|
heck = "0.4.1"
|
||||||
|
logos = "0.13.0"
|
||||||
|
strong-xml = "0.6.3"
|
||||||
230
crates/generate/src/generate.rs
Normal file
230
crates/generate/src/generate.rs
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
use crate::{
|
||||||
|
parse::{Expr, Stmt},
|
||||||
|
Intrinsic,
|
||||||
|
};
|
||||||
|
use eyre::{bail, Context, OptionExt, Result};
|
||||||
|
|
||||||
|
pub fn generate(intrinsics: &[Intrinsic]) -> Result<()> {
|
||||||
|
println!("impl<C: super::Core> Intrinsics for C {{}}");
|
||||||
|
println!("trait Intrinsics: super::Core {{");
|
||||||
|
for intr in intrinsics {
|
||||||
|
generate_intr(intr).wrap_err_with(|| format!("generating `{}`", intr.name))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("}}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_intr(intr: &Intrinsic) -> Result<(), eyre::Error> {
|
||||||
|
let signature = signature(intr)?;
|
||||||
|
println!(" {signature} {{");
|
||||||
|
let body = generate_body(intr).wrap_err("generating body")?;
|
||||||
|
println!("{}", indent(&body, 8));
|
||||||
|
println!(" }}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indent(input: &str, indent: usize) -> String {
|
||||||
|
let replace = format!("\n{}", " ".repeat(indent));
|
||||||
|
let mut s = " ".repeat(indent);
|
||||||
|
s.push_str(&input.trim_end().replace("\n", &replace));
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VariableType {
|
||||||
|
is_signed: bool,
|
||||||
|
elem_width: u64,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
full_width: u64,
|
||||||
|
raw_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableType {
|
||||||
|
fn of(etype: &str, ty: &str) -> Result<Self> {
|
||||||
|
let full_width = match ty {
|
||||||
|
"__m128i" => 128,
|
||||||
|
_ => bail!("unknown type: {ty}"),
|
||||||
|
};
|
||||||
|
let (is_signed, elem_width) = match etype {
|
||||||
|
"SI16" => (true, 16),
|
||||||
|
"UI8" => (false, 8),
|
||||||
|
_ => bail!("unknown element type: {etype}"),
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
is_signed,
|
||||||
|
full_width,
|
||||||
|
elem_width,
|
||||||
|
raw_type: ty.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rust_type(&self) -> String {
|
||||||
|
let pre = if self.is_signed { 'i' } else { 'u' };
|
||||||
|
format!("{pre}{}", self.elem_width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_body(instr: &Intrinsic) -> Result<String> {
|
||||||
|
let opstmts = parse_op(instr)?;
|
||||||
|
let mut rust_stmts = Vec::<String>::new();
|
||||||
|
|
||||||
|
let type_of_ident = |ident: &str| -> Result<VariableType> {
|
||||||
|
for param in &instr.parameter {
|
||||||
|
if param.varname.as_deref() == Some(ident) {
|
||||||
|
return VariableType::of(
|
||||||
|
param.etype.as_deref().ok_or_eyre("no param etype")?,
|
||||||
|
param.r#type.as_deref().ok_or_eyre("no param type")?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if instr.ret.varname.as_deref() == Some(ident) {
|
||||||
|
return VariableType::of(
|
||||||
|
instr.ret.etype.as_deref().ok_or_eyre("no param etype")?,
|
||||||
|
instr.ret.r#type.as_deref().ok_or_eyre("no param type")?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("variable {ident} not found in pseudocode");
|
||||||
|
};
|
||||||
|
|
||||||
|
for stmt in opstmts {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Assign { lhs, rhs } => {
|
||||||
|
let Expr::Index { lhs, idx } = lhs else {
|
||||||
|
bail!("lhs of assign must be indexing");
|
||||||
|
};
|
||||||
|
let Expr::Ident(ident) = *lhs else {
|
||||||
|
bail!("lhs of indexing must be identifier");
|
||||||
|
};
|
||||||
|
let Expr::Range { left, right } = *idx else {
|
||||||
|
bail!("idx argument must be range");
|
||||||
|
};
|
||||||
|
let Expr::Int(high) = *left else {
|
||||||
|
bail!("lhs of range must be int");
|
||||||
|
};
|
||||||
|
let Expr::Int(low) = *right else {
|
||||||
|
bail!("rhs of range must be int");
|
||||||
|
};
|
||||||
|
if high < low {
|
||||||
|
bail!("range must be HIGH:LOW, but was {high}:{low}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = high - low + 1; // (inclusive)
|
||||||
|
if !size.is_power_of_two() {
|
||||||
|
bail!("indexing size must be power of two");
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = type_of_ident(&ident)?;
|
||||||
|
if size != ty.elem_width {
|
||||||
|
bail!(
|
||||||
|
"unsupported not-direct element indexing, size={size}, element size={}",
|
||||||
|
ty.elem_width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let expr = generate_expr_tmp(&mut rust_stmts, rhs, &type_of_ident)?;
|
||||||
|
let raw = &ty.raw_type;
|
||||||
|
let rust_type = ty.rust_type();
|
||||||
|
let lane_idx = low / ty.elem_width;
|
||||||
|
rust_stmts.push(format!(
|
||||||
|
"self.set_lane_{raw}_{rust_type}({ident}, {lane_idx}, {expr});"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(rust_stmts.join("\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr_tmp(
|
||||||
|
rust_stmts: &mut Vec<String>,
|
||||||
|
expr: Expr,
|
||||||
|
type_of_ident: &impl Fn(&str) -> Result<VariableType>,
|
||||||
|
) -> Result<String> {
|
||||||
|
let result = match expr {
|
||||||
|
Expr::Int(int) => int.to_string(),
|
||||||
|
Expr::Ident(_) => todo!(),
|
||||||
|
Expr::Index { lhs, idx } => {
|
||||||
|
let Expr::Ident(ident) = *lhs else {
|
||||||
|
bail!("lhs of indexing must be identifier");
|
||||||
|
};
|
||||||
|
let Expr::Range { left, right } = *idx else {
|
||||||
|
bail!("idx argument must be range");
|
||||||
|
};
|
||||||
|
let Expr::Int(high) = *left else {
|
||||||
|
bail!("lhs of range must be int");
|
||||||
|
};
|
||||||
|
let Expr::Int(low) = *right else {
|
||||||
|
bail!("rhs of range must be int");
|
||||||
|
};
|
||||||
|
if high < low {
|
||||||
|
bail!("range must be HIGH:LOW, but was {high}:{low}");
|
||||||
|
}
|
||||||
|
let size = high - low + 1; // (inclusive)
|
||||||
|
if !size.is_power_of_two() {
|
||||||
|
bail!("indexing size must be power of two");
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = type_of_ident(&ident)?;
|
||||||
|
if size != ty.elem_width {
|
||||||
|
bail!(
|
||||||
|
"unsupported not-direct element indexing, size={size}, element size={}",
|
||||||
|
ty.elem_width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let raw = &ty.raw_type;
|
||||||
|
let rust_type = ty.rust_type();
|
||||||
|
let lane_idx = low / ty.elem_width;
|
||||||
|
let stmt = format!("let __tmp = self.get_lane_{raw}_{rust_type}({ident}, {lane_idx});");
|
||||||
|
rust_stmts.push(stmt);
|
||||||
|
"__tmp".into()
|
||||||
|
}
|
||||||
|
Expr::Range { .. } => todo!(),
|
||||||
|
Expr::Call { function, args } => {
|
||||||
|
let function = heck::AsSnekCase(function);
|
||||||
|
let args = args
|
||||||
|
.into_iter()
|
||||||
|
.map(|arg| generate_expr_tmp(rust_stmts, arg, type_of_ident))
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
.join(", ");
|
||||||
|
let stmt = format!("let __tmp = self.{function}({args});");
|
||||||
|
rust_stmts.push(stmt);
|
||||||
|
"__tmp".into()
|
||||||
|
}
|
||||||
|
Expr::BinaryOp { .. } => todo!(),
|
||||||
|
};
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_op(intr: &Intrinsic) -> Result<Vec<Stmt>> {
|
||||||
|
let Some(operation) = &intr.operation else {
|
||||||
|
bail!("intrinsic {} has no operation", intr.name);
|
||||||
|
};
|
||||||
|
let stmts = crate::parse::parse_operation(&operation.value).wrap_err("parsing intrinsic")?;
|
||||||
|
Ok(stmts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(intr: &Intrinsic) -> Result<String> {
|
||||||
|
let name = &intr.name;
|
||||||
|
|
||||||
|
let args = intr
|
||||||
|
.parameter
|
||||||
|
.iter()
|
||||||
|
.map(|param| {
|
||||||
|
format!(
|
||||||
|
"{}: Self::{}",
|
||||||
|
param.varname.as_ref().unwrap(),
|
||||||
|
param.r#type.as_ref().unwrap()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
let ret_name = intr.ret.varname.as_ref().unwrap();
|
||||||
|
let ret_ty = intr.ret.r#type.as_ref().unwrap();
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"fn {name}(&mut self, {ret_name}: &mut Self::{ret_ty}, {args})"
|
||||||
|
))
|
||||||
|
}
|
||||||
87
crates/generate/src/main.rs
Normal file
87
crates/generate/src/main.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
mod generate;
|
||||||
|
mod parse;
|
||||||
|
|
||||||
|
use eyre::{Context, Result};
|
||||||
|
use strong_xml::XmlRead;
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "intrinsics_list")]
|
||||||
|
struct IntrinsicsList {
|
||||||
|
#[xml(child = "intrinsic")]
|
||||||
|
intrinsics: Vec<Intrinsic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "intrinsic")]
|
||||||
|
struct Intrinsic {
|
||||||
|
#[xml(attr = "name")]
|
||||||
|
name: String,
|
||||||
|
#[xml(child = "return")]
|
||||||
|
ret: Return,
|
||||||
|
#[xml(child = "parameter")]
|
||||||
|
parameter: Vec<Parameter>,
|
||||||
|
#[xml(child = "operation")]
|
||||||
|
operation: Option<Operation>,
|
||||||
|
#[xml(child = "CPUID")]
|
||||||
|
cpuid: Vec<CpuId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "return")]
|
||||||
|
struct Return {
|
||||||
|
/// Element type
|
||||||
|
#[xml(attr = "etype")]
|
||||||
|
etype: Option<String>,
|
||||||
|
#[xml(attr = "type")]
|
||||||
|
r#type: Option<String>,
|
||||||
|
#[xml(attr = "varname")]
|
||||||
|
varname: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "parameter")]
|
||||||
|
struct Parameter {
|
||||||
|
/// Element type
|
||||||
|
#[xml(attr = "etype")]
|
||||||
|
etype: Option<String>,
|
||||||
|
#[xml(attr = "type")]
|
||||||
|
r#type: Option<String>,
|
||||||
|
#[xml(attr = "varname")]
|
||||||
|
varname: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "operation")]
|
||||||
|
struct Operation {
|
||||||
|
#[xml(text)]
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlRead)]
|
||||||
|
#[xml(tag = "CPUID")]
|
||||||
|
struct CpuId {
|
||||||
|
#[xml(text)]
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let data = std::fs::read_to_string("intrinsics.xml")
|
||||||
|
.wrap_err("unable to find intrinsics.xml, get the file from the intel intrinsics guide")?;
|
||||||
|
|
||||||
|
let IntrinsicsList { intrinsics: list } =
|
||||||
|
IntrinsicsList::from_str(&data).wrap_err("failed to parse intrinsics.xml")?;
|
||||||
|
|
||||||
|
eprintln!("loaded {} intrinsics", list.len());
|
||||||
|
|
||||||
|
let list = list
|
||||||
|
.into_iter()
|
||||||
|
.filter(|intr| intr.cpuid.iter().any(|cpu| !cpu.value.contains("AVX512")))
|
||||||
|
.filter(|intr| intr.name == "_mm_packus_epi16")
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
eprintln!("filtered: {}", list.len());
|
||||||
|
|
||||||
|
generate::generate(&list).wrap_err("generating rust code")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
215
crates/generate/src/parse.rs
Normal file
215
crates/generate/src/parse.rs
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
use eyre::{bail, ContextCompat, OptionExt, Result};
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Stmt {
|
||||||
|
FnDef,
|
||||||
|
Assign {
|
||||||
|
lhs: Expr,
|
||||||
|
rhs: Expr,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
cond: Expr,
|
||||||
|
then: Expr,
|
||||||
|
els: Option<Expr>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Expr {
|
||||||
|
Int(u64),
|
||||||
|
Ident(String),
|
||||||
|
Index {
|
||||||
|
lhs: Box<Expr>,
|
||||||
|
idx: Box<Expr>,
|
||||||
|
},
|
||||||
|
Range {
|
||||||
|
left: Box<Expr>,
|
||||||
|
right: Box<Expr>,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
function: String,
|
||||||
|
args: Vec<Expr>,
|
||||||
|
},
|
||||||
|
BinaryOp {
|
||||||
|
op: BinaryOpKind,
|
||||||
|
lhs: Box<Expr>,
|
||||||
|
rhs: Box<Expr>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum BinaryOpKind {}
|
||||||
|
|
||||||
|
pub(crate) fn parse_operation(op: &str) -> Result<Vec<Stmt>> {
|
||||||
|
let tokens = Token::lexer(op.trim())
|
||||||
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
|
.map_err(|()| eyre::eyre!("failed to lex"))?;
|
||||||
|
let stmts = parse(tokens)?;
|
||||||
|
|
||||||
|
Ok(stmts)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser {
|
||||||
|
tokens: std::iter::Peekable<std::vec::IntoIter<Token>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
fn next(&mut self) -> Result<Token> {
|
||||||
|
self.tokens.next().ok_or_eyre("reached EOF")
|
||||||
|
}
|
||||||
|
fn peek(&mut self) -> Result<&Token> {
|
||||||
|
self.tokens.peek().ok_or_eyre("reached EOF")
|
||||||
|
}
|
||||||
|
fn expect(&mut self, token: Token) -> Result<()> {
|
||||||
|
let n = self.next()?;
|
||||||
|
if n != token {
|
||||||
|
bail!("expected {token:?}, found {n:?}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn eat(&mut self, token: Token) -> bool {
|
||||||
|
if self.tokens.peek() == Some(&token) {
|
||||||
|
self.tokens.next();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(tokens: Vec<Token>) -> Result<Vec<Stmt>> {
|
||||||
|
let parser = &mut Parser {
|
||||||
|
tokens: tokens.into_iter().peekable(),
|
||||||
|
};
|
||||||
|
let mut stmts = Vec::new();
|
||||||
|
|
||||||
|
while parser.tokens.peek().is_some() {
|
||||||
|
let stmt = parse_stmt(parser)?;
|
||||||
|
stmts.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(stmts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_stmt(parser: &mut Parser) -> Result<Stmt> {
|
||||||
|
let stmt = match parser.peek() {
|
||||||
|
_ => {
|
||||||
|
let expr = parse_expr(parser)?;
|
||||||
|
|
||||||
|
if parser.eat(Token::Assign) {
|
||||||
|
let rhs = parse_expr(parser)?;
|
||||||
|
|
||||||
|
Stmt::Assign { lhs: expr, rhs }
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parser.eat(Token::Newline);
|
||||||
|
Ok(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr(parser: &mut Parser) -> Result<Expr> {
|
||||||
|
parse_expr_range(parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_range(parser: &mut Parser) -> Result<Expr> {
|
||||||
|
let expr = parse_expr_call(parser)?;
|
||||||
|
if parser.eat(Token::Colon) {
|
||||||
|
let rhs = parse_expr_call(parser)?;
|
||||||
|
Ok(Expr::Range {
|
||||||
|
left: Box::new(expr),
|
||||||
|
right: Box::new(rhs),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_call(parser: &mut Parser) -> Result<Expr> {
|
||||||
|
let mut lhs = parse_expr_atom(parser)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if parser.eat(Token::ParenOpen) {
|
||||||
|
let arg = parse_expr(parser)?;
|
||||||
|
parser.expect(Token::ParenClose);
|
||||||
|
let Expr::Ident(ident) = lhs else {
|
||||||
|
panic!("lhs of function must be ident");
|
||||||
|
};
|
||||||
|
lhs = Expr::Call {
|
||||||
|
function: ident,
|
||||||
|
args: vec![arg],
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if parser.eat(Token::BracketOpen) {
|
||||||
|
let arg = parse_expr(parser)?;
|
||||||
|
parser.expect(Token::BracketClose);
|
||||||
|
lhs = Expr::Index {
|
||||||
|
lhs: Box::new(lhs),
|
||||||
|
idx: Box::new(arg),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(lhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_atom(parser: &mut Parser) -> Result<Expr> {
|
||||||
|
let token = parser.next()?;
|
||||||
|
Ok(match token {
|
||||||
|
Token::Ident(ident) => Expr::Ident(ident),
|
||||||
|
Token::Integer(int) => Expr::Int(int),
|
||||||
|
_ => panic!("unexpected token in expr position: {token:?}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Logos, PartialEq, Eq)]
|
||||||
|
enum Token {
|
||||||
|
#[regex("//[^\n]*\n?", logos::skip)]
|
||||||
|
#[regex(r"[ \t\r]+", logos::skip)]
|
||||||
|
Comment,
|
||||||
|
|
||||||
|
#[token("(")]
|
||||||
|
ParenOpen,
|
||||||
|
#[token("[")]
|
||||||
|
BracketOpen,
|
||||||
|
#[token(")")]
|
||||||
|
ParenClose,
|
||||||
|
#[token("]")]
|
||||||
|
BracketClose,
|
||||||
|
|
||||||
|
#[token("DEFINE")]
|
||||||
|
Define,
|
||||||
|
#[token("CASE")]
|
||||||
|
Case,
|
||||||
|
#[token("ESAC")]
|
||||||
|
Esac,
|
||||||
|
#[token("IF")]
|
||||||
|
If,
|
||||||
|
#[token("FI")]
|
||||||
|
Fi,
|
||||||
|
#[token("RETURN")]
|
||||||
|
Return,
|
||||||
|
#[token("OF")]
|
||||||
|
Of,
|
||||||
|
|
||||||
|
#[token("\n")]
|
||||||
|
Newline,
|
||||||
|
|
||||||
|
#[token(",")]
|
||||||
|
Comma,
|
||||||
|
#[token(":=")]
|
||||||
|
Assign,
|
||||||
|
#[token(":")]
|
||||||
|
Colon,
|
||||||
|
|
||||||
|
#[regex(r"[a-zA-Z_]\w*", |lex| lex.slice().to_owned())]
|
||||||
|
Ident(String),
|
||||||
|
|
||||||
|
#[regex(r"\d+", |lex| lex.slice().parse().ok())]
|
||||||
|
Integer(u64),
|
||||||
|
}
|
||||||
8
crates/intringen/Cargo.toml
Normal file
8
crates/intringen/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "intringen"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
3
crates/intringen/src/lib.rs
Normal file
3
crates/intringen/src/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod x86;
|
||||||
|
|
||||||
|
pub trait Lanes<const N: usize, Elem> {}
|
||||||
53
crates/intringen/src/x86/generated.rs
Normal file
53
crates/intringen/src/x86/generated.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
impl<C: super::Core> Intrinsics for C {}
|
||||||
|
trait Intrinsics: super::Core {
|
||||||
|
fn _mm_packus_epi16(&mut self, dst: &mut Self::__m128i, a: Self::__m128i, b: Self::__m128i) {
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 0);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 0, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 1);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 1, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 2);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 2, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 3);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 3, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 4);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 4, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 5);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 5, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 6);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 6, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(a, 7);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 7, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 0);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 8, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 1);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 9, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 2);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 10, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 3);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 11, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 4);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 12, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 5);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 13, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 6);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 14, __tmp);
|
||||||
|
let __tmp = self.get_lane___m128i_i16(b, 7);
|
||||||
|
let __tmp = self.saturate_u8(__tmp);
|
||||||
|
self.set_lane___m128i_u8(dst, 15, __tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
crates/intringen/src/x86/mod.rs
Normal file
49
crates/intringen/src/x86/mod.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#![allow(non_camel_case_types, non_snake_case)]
|
||||||
|
|
||||||
|
mod generated;
|
||||||
|
|
||||||
|
pub trait Core {
|
||||||
|
type __m128i: Copy;
|
||||||
|
|
||||||
|
fn get_lane___m128i_u16(&mut self, value: Self::__m128i, idx: u64) -> u16;
|
||||||
|
fn get_lane___m128i_i16(&mut self, value: Self::__m128i, idx: u64) -> i16;
|
||||||
|
|
||||||
|
fn set_lane___m128i_u8(&mut self, place: &mut Self::__m128i, idx: u64, value: u8);
|
||||||
|
fn set_lane___m128i_i8(&mut self, place: &mut Self::__m128i, idx: u64, value: i8);
|
||||||
|
|
||||||
|
fn saturate_u8(&mut self, elem: i16) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ValueCore;
|
||||||
|
|
||||||
|
impl Core for ValueCore {
|
||||||
|
type __m128i = [u8; 16];
|
||||||
|
|
||||||
|
fn get_lane___m128i_u16(&mut self, value: Self::__m128i, idx: u64) -> u16 {
|
||||||
|
let first = value[(idx * 2) as usize];
|
||||||
|
let second = value[(idx * 2 + 1) as usize];
|
||||||
|
// todo: le? be?
|
||||||
|
((first << 8) as u16) | (second as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_lane___m128i_i16(&mut self, value: Self::__m128i, idx: u64) -> i16 {
|
||||||
|
let first = value[(idx * 2) as usize];
|
||||||
|
let second = value[(idx * 2 + 1) as usize];
|
||||||
|
// todo: le? be?
|
||||||
|
(((first << 8) as u16) | (second as u16)) as i16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_lane___m128i_u8(&mut self, place: &mut Self::__m128i, idx: u64, value: u8) {
|
||||||
|
place[idx as usize] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_lane___m128i_i8(&mut self, place: &mut Self::__m128i, idx: u64, value: i8) {
|
||||||
|
place[idx as usize] = value as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn saturate_u8(&mut self, elem: i16) -> u8 {
|
||||||
|
let clamp = elem.clamp(0, u8::MAX as i16);
|
||||||
|
clamp as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait Lanes<const N: usize, Elem> {}
|
||||||
3
shell.nix
Normal file
3
shell.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
|
||||||
|
packages = with pkgs; [ rustup ];
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue