use super::CodeGen;
use super::types::{RegisterType, SpecSignature};
use crate::ast::{Statement, WordDef};
use crate::types::StackType;
pub(super) const SPECIALIZABLE_OPS: &[&str] = &[
"i.+",
"i.add",
"i.-",
"i.subtract",
"i.*",
"i.multiply",
"i./",
"i.divide",
"i.%",
"i.mod",
"band",
"bor",
"bxor",
"bnot",
"shl",
"shr",
"popcount",
"clz",
"ctz",
"int->float",
"float->int",
"and",
"or",
"not",
"i.<",
"i.lt",
"i.>",
"i.gt",
"i.<=",
"i.lte",
"i.>=",
"i.gte",
"i.=",
"i.eq",
"i.<>",
"i.neq",
"f.+",
"f.add",
"f.-",
"f.subtract",
"f.*",
"f.multiply",
"f./",
"f.divide",
"f.<",
"f.lt",
"f.>",
"f.gt",
"f.<=",
"f.lte",
"f.>=",
"f.gte",
"f.=",
"f.eq",
"f.<>",
"f.neq",
"dup",
"drop",
"swap",
"over",
"rot",
"nip",
"tuck",
"pick",
"roll",
];
impl CodeGen {
pub fn can_specialize(&self, word: &WordDef) -> Option<SpecSignature> {
let effect = word.effect.as_ref()?;
if !effect.is_pure() {
return None;
}
let inputs = Self::extract_register_types(&effect.inputs)?;
let outputs = Self::extract_register_types(&effect.outputs)?;
if inputs.is_empty() && outputs.is_empty() {
return None;
}
if outputs.is_empty() {
return None;
}
if !self.is_body_specializable(&word.body, &word.name) {
return None;
}
Some(SpecSignature { inputs, outputs })
}
fn extract_register_types(stack: &StackType) -> Option<Vec<RegisterType>> {
let mut types = Vec::new();
let mut current = stack;
loop {
match current {
StackType::Empty => break,
StackType::RowVar(_) => {
break;
}
StackType::Cons { rest, top } => {
let reg_ty = RegisterType::from_type(top)?;
types.push(reg_ty);
current = rest;
}
}
}
types.reverse();
Some(types)
}
pub(super) fn is_body_specializable(&self, body: &[Statement], word_name: &str) -> bool {
let mut prev_was_int_literal = false;
for stmt in body {
if !self.is_statement_specializable(stmt, word_name, prev_was_int_literal) {
return false;
}
prev_was_int_literal = matches!(stmt, Statement::IntLiteral(_));
}
true
}
fn is_statement_specializable(
&self,
stmt: &Statement,
word_name: &str,
prev_was_int_literal: bool,
) -> bool {
match stmt {
Statement::IntLiteral(_) => true,
Statement::FloatLiteral(_) => true,
Statement::BoolLiteral(_) => true,
Statement::StringLiteral(_) => false,
Statement::Symbol(_) => false,
Statement::Quotation { .. } => false,
Statement::Match { .. } => false,
Statement::WordCall { name, .. } => {
if name == word_name {
return true;
}
if (name == "pick" || name == "roll") && !prev_was_int_literal {
return false;
}
if SPECIALIZABLE_OPS.contains(&name.as_str()) {
return true;
}
if self.specialized_words.contains_key(name) {
return true;
}
false
}
Statement::If {
then_branch,
else_branch,
span: _,
} => {
if !self.is_body_specializable(then_branch, word_name) {
return false;
}
if let Some(else_stmts) = else_branch
&& !self.is_body_specializable(else_stmts, word_name)
{
return false;
}
true
}
}
}
}