diff --git a/src/panfrost/compiler/kraid/proc/isa/encoder.rs b/src/panfrost/compiler/kraid/proc/isa/encoder.rs index bafec5dcf81..bf0860274fd 100644 --- a/src/panfrost/compiler/kraid/proc/isa/encoder.rs +++ b/src/panfrost/compiler/kraid/proc/isa/encoder.rs @@ -14,6 +14,7 @@ pub fn gen_encoder( let mut ts = quote! { use crate::isa::*; }; + declare_expr_helpers(&mut ts); isa.enums .add_meta_enum( diff --git a/src/panfrost/compiler/kraid/proc/isa/expr.rs b/src/panfrost/compiler/kraid/proc/isa/expr.rs new file mode 100644 index 00000000000..8cf9b607a0c --- /dev/null +++ b/src/panfrost/compiler/kraid/proc/isa/expr.rs @@ -0,0 +1,283 @@ +// Copyright © 2026 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +use crate::isa::xml::XmlElement; +use crate::isa::*; +use proc_macro2::Ident; +use proc_macro2::TokenStream as TokenStream2; +use std::rc::Rc; + +pub struct FieldIdent { + pub name: String, + pub ident: Ident, +} + +impl ToTokens for FieldIdent { + fn to_tokens(&self, ts: &mut TokenStream2) { + self.ident.to_tokens(ts) + } +} + +pub struct LiteralEnum { + pub enum_type: Rc, + pub value_name: String, + pub value_ident: Ident, +} + +impl ToTokens for LiteralEnum { + fn to_tokens(&self, ts: &mut TokenStream2) { + let e_ident = &self.enum_type.ident; + let v_ident = &self.value_ident; + ts.extend(quote! { + #e_ident::#v_ident + }); + } +} + +pub enum Expr { + Ident(FieldIdent), + EnumLiteral(LiteralEnum), + UintLiteral(u32), + If(Vec), + BitwiseAnd([Box; 2]), + BitwiseOr([Box; 2]), + BitwiseXor([Box; 2]), + Equal([Box; 2]), + LessThan([Box; 2]), + LessEqual([Box; 2]), + NotEqual([Box; 2]), + LogicalAnd([Box; 2]), + Inside(Vec), + Add([Box; 2]), + Sub([Box; 2]), + RShift([Box; 2]), + LShift([Box; 2]), + CountOnes(Box), + Mask(Box), + Slice([Box; 3]), +} + +impl Expr { + pub(super) fn literal( + type_: Option<&str>, + value: &str, + enums: &EnumSet, + ) -> Result { + if let Some(e) = type_.and_then(|t| enums.get_enum(t)) { + if let Some(v) = e.get_value(value) { + return Ok(Expr::EnumLiteral(LiteralEnum { + enum_type: e.clone(), + value_name: v.name.clone(), + value_ident: v.ident.clone(), + })); + } + } + + if let Ok(v) = u32::from_str_radix(value, 10) { + Ok(Expr::UintLiteral(v)) + } else { + Err(err("Unknown expression literal")) + } + } + + pub(super) fn from_xml(xml: XmlElement, enums: &EnumSet) -> Result { + assert_eq!(xml.name.local_name, "expression"); + + if let Some(op) = xml.attrs.get("operator") { + let mut operands = Vec::new(); + for c in xml.children { + operands.push(Expr::from_xml(c, enums)?); + } + + // Check the number of arguments now so we can assume it later + let op_err = "Invalid number of expression children"; + match op.as_str() { + "if" => { + if operands.len() < 3 || operands.len() % 2 != 1 { + return Err(op_err.into()); + } + Ok(Expr::If(operands)) + } + "bitwise_and" | "bitwise_or" | "add" => { + let y = Box::new(operands.pop().ok_or(op_err)?); + let x = Box::new(operands.pop().ok_or(op_err)?); + let mut y = Box::new(match op.as_str() { + "bitwise_and" => Expr::BitwiseAnd([x, y]), + "bitwise_or" => Expr::BitwiseOr([x, y]), + "add" => Expr::Add([x, y]), + _ => panic!("Unknown expression operator: {op}"), + }); + while let Some(x) = operands.pop() { + let x = Box::new(x); + y = Box::new(match op.as_str() { + "bitwise_and" => Expr::BitwiseAnd([x, y]), + "bitwise_or" => Expr::BitwiseOr([x, y]), + "add" => Expr::Add([x, y]), + _ => panic!("Unknown expression operator: {op}"), + }); + } + Ok(*y) + } + "bitwise_xor" | "equal" | "not_equal" | "less_equal" + | "less_than" | "greater_than" | "greater_equal" + | "logical_and" | "sub" | "rshift" | "lshift" => { + let mut iter = operands.into_iter(); + let x = Box::new(iter.next().ok_or(op_err)?); + let y = Box::new(iter.next().ok_or(op_err)?); + if iter.next().is_some() { + return Err(op_err.into()); + } + match op.as_str() { + "bitwise_xor" => Ok(Expr::BitwiseXor([x, y])), + "equal" => Ok(Expr::Equal([x, y])), + "greater_equal" => Ok(Expr::LessEqual([y, x])), + "greater_than" => Ok(Expr::LessThan([y, x])), + "less_equal" => Ok(Expr::LessEqual([x, y])), + "less_than" => Ok(Expr::LessThan([x, y])), + "not_equal" => Ok(Expr::NotEqual([x, y])), + "logical_and" => Ok(Expr::LogicalAnd([x, y])), + "sub" => Ok(Expr::Sub([x, y])), + "rshift" => Ok(Expr::RShift([x, y])), + "lshift" => Ok(Expr::LShift([x, y])), + _ => panic!("Unknown expression operator: {op}"), + } + } + "inside" => { + if operands.len() < 2 { + return Err(op_err.into()); + } + for i in 1..operands.len() { + if !matches!( + operands[i], + Expr::EnumLiteral(_) | Expr::UintLiteral(_), + ) { + return Err( + "inside expression operands should be literals" + .into(), + ); + } + } + Ok(Expr::Inside(operands)) + } + "countones" | "mask" => { + let mut iter = operands.into_iter(); + let x = Box::new(iter.next().ok_or(op_err)?); + if iter.next().is_some() { + return Err(op_err.into()); + } + match op.as_str() { + "countones" => Ok(Expr::CountOnes(x)), + "mask" => Ok(Expr::Mask(x)), + _ => panic!("Unknown expression operator: {op}"), + } + } + "slice" => { + let mut iter = operands.into_iter(); + let x = Box::new(iter.next().ok_or(op_err)?); + let y = Box::new(iter.next().ok_or(op_err)?); + let z = Box::new(iter.next().ok_or(op_err)?); + if iter.next().is_some() { + return Err(op_err.into()); + } + Ok(Expr::Slice([x, y, z])) + } + _ => Err("Unknown expression".into()), + } + } else if let Some(name) = xml.attrs.get("identifier") { + Ok(Expr::Ident(FieldIdent { + name: name.to_string(), + ident: instr_field_ident(name), + })) + } else if let Some(literal) = xml.attrs.get("literal") { + let type_ = xml + .attrs + .get("type") + .or_else(|| xml.attrs.get("enum")) + .ok_or(err("A literal must have a type or an enum"))?; + + Expr::literal(Some(type_), literal, enums) + } else { + Err(err("Unknown expression type")) + } + } + + pub fn as_enum(&self) -> Option<&LiteralEnum> { + match self { + Expr::EnumLiteral(lit) => Some(lit), + _ => None, + } + } +} + +pub fn declare_expr_helpers(ts: &mut TokenStream2) { + ts.extend(quote! { + fn expr_mask_u32(bits: u32) -> u32 { + if bits < 32 { + (1_u32 << bits) - 1 + } else { + !0_u32 + } + } + + fn expr_slice_u32(x: u32, high_bit: u32, low_bit: u32) -> u32 { + (x >> low_bit) & expr_mask_u32(high_bit + 1 - low_bit) + } + }); +} + +impl ToTokens for Expr { + fn to_tokens(&self, ts: &mut TokenStream2) { + ts.extend(match self { + Expr::Ident(ident) => { + quote! { u32::from(#ident) } + } + Expr::EnumLiteral(lit) => { + quote! { + u32::from(#lit.try_encode(arch)?) + } + } + Expr::UintLiteral(u) => quote! { #u }, + Expr::If(args) => { + let mut iter = args.iter(); + let c = iter.next().unwrap(); + let v = iter.next().unwrap(); + let mut val_ts = quote! { if #c { #v } }; + loop { + let c = iter.next().unwrap(); + if let Some(v) = iter.next() { + val_ts.extend(quote! { else if #c { #v } }); + } else { + val_ts.extend(quote! { else { #c } }); + break; + } + } + val_ts + } + Expr::BitwiseAnd([x, y]) => quote! { (#x) & (#y) }, + Expr::BitwiseOr([x, y]) => quote! { (#x) | (#y) }, + Expr::BitwiseXor([x, y]) => quote! { (#x) ^ (#y) }, + Expr::Equal([x, y]) => quote! { (#x) == (#y) }, + Expr::LessThan([x, y]) => quote! { (#x) < (#y) }, + Expr::LessEqual([x, y]) => quote! { (#x) <= (#y) }, + Expr::NotEqual([x, y]) => quote! { (#x) != (#y) }, + Expr::LogicalAnd([x, y]) => quote! { (#x) && (#y) }, + Expr::Add([x, y]) => quote! { (#x) + (#y) }, + Expr::Sub([x, y]) => quote! { (#x) - (#y) }, + Expr::RShift([x, y]) => quote! { (#x) >> (#y) }, + Expr::LShift([x, y]) => quote! { (#x) << (#y) }, + Expr::Inside(args) => { + let mut iter = args.iter(); + let x = iter.next().unwrap(); + let v = iter.next().unwrap(); + let mut vals_ts = quote! { #v }; + for v in iter { + vals_ts.extend(quote! { , #v }); + } + quote! { [#vals_ts].contains(&(#x)) } + } + Expr::CountOnes(x) => quote! { (#x).count_ones() }, + Expr::Mask(x) => quote! { expr_mask_u32(#x) }, + Expr::Slice([v, h, l]) => quote! { expr_slice_u32(#v, #h, #l) }, + }); + } +} diff --git a/src/panfrost/compiler/kraid/proc/isa/mod.rs b/src/panfrost/compiler/kraid/proc/isa/mod.rs index 9f72508bfcb..24a822d128f 100644 --- a/src/panfrost/compiler/kraid/proc/isa/mod.rs +++ b/src/panfrost/compiler/kraid/proc/isa/mod.rs @@ -3,6 +3,7 @@ pub mod encoder; pub mod enums; +pub mod expr; mod xml; use proc_macro2::TokenStream as TokenStream2; @@ -11,6 +12,7 @@ use std::ops::Range; use xml::XmlElement; pub use enums::*; +pub use expr::*; #[macro_export] macro_rules! ident {