generate _mm_packus_epi16

This commit is contained in:
nora 2024-01-05 21:40:01 +01:00
commit 429d9b826c
13 changed files with 855 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
intrinsics.xml

189
Cargo.lock generated Normal file
View 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
View file

@ -0,0 +1,3 @@
[workspace]
members = ["crates/generate", "crates/intringen"]
resolver = "2"

View 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"

View 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})"
))
}

View 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(())
}

View 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),
}

View 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]

View file

@ -0,0 +1,3 @@
pub mod x86;
pub trait Lanes<const N: usize, Elem> {}

View 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);
}
}

View 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
View file

@ -0,0 +1,3 @@
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
packages = with pkgs; [ rustup ];
}