use super::{PARAM_BOUNDS, SAT_BOUNDS, is_identity_peel_method};
pub(super) fn is_u64_width_bounds(lo: i64, hi: i64) -> bool {
lo == 0 && hi == -1
}
pub(super) fn rust_int_bounds(ty: &str) -> Option<(i64, i64)> {
let base = ty.rsplit("::").next().unwrap_or(ty).trim();
match base {
"i8" => Some((i8::MIN as i64, i8::MAX as i64)),
"i16" => Some((i16::MIN as i64, i16::MAX as i64)),
"i32" => Some((i32::MIN as i64, i32::MAX as i64)),
"i64" | "isize" => Some((i64::MIN, i64::MAX)),
"u8" => Some((0, u8::MAX as i64)),
"u16" => Some((0, u16::MAX as i64)),
"u32" => Some((0, u32::MAX as i64)),
"u64" | "usize" => Some((0, -1)),
"u128" => Some((0, -1)),
"i128" => Some((i64::MIN, i64::MAX)),
"NonZeroU8" => Some((1, u8::MAX as i64)),
"NonZeroU16" => Some((1, u16::MAX as i64)),
"NonZeroU32" => Some((1, u32::MAX as i64)),
"NonZeroU64" | "NonZeroUsize" | "NonZeroU128" => Some((1, i64::MAX)),
_ => None,
}
}
pub(super) fn wrap_width(lo: i64, hi: i64) -> Option<(u32, Option<i64>, bool)> {
if is_u64_width_bounds(lo, hi) {
return Some((64, None, false));
}
let signed = lo != 0;
if signed && lo == i64::MIN && hi == i64::MAX {
return Some((64, None, true));
}
let modulus = if signed {
hi.checked_sub(lo).and_then(|d| d.checked_add(1))?
} else {
hi.checked_add(1)?
};
if modulus <= 0 || !(modulus as u64).is_power_of_two() {
return None;
}
Some(((modulus as u64).trailing_zeros(), Some(modulus), signed))
}
pub(super) struct RotWrap {
pub(super) bits: u32,
pub(super) mslot: usize,
pub(super) signed: bool,
pub(super) hi: i64,
}
pub(super) fn emit_rotl_bits(
u_in: usize,
k_left: u32,
w: &RotWrap,
lines: &mut Vec<String>,
next: &mut usize,
) -> Option<usize> {
if k_left == 0 || k_left >= w.bits {
if !w.signed {
return Some(u_in);
}
let his = *next;
*next += 1;
lines.push(format!("${his} = const {} : Int", w.hi));
let gt = *next;
*next += 1;
lines.push(format!("${gt} = cmp gt ${u_in} ${his} : Bool"));
let adj = *next;
*next += 1;
lines.push(format!("${adj} = arith mul ${gt} ${} : Int", w.mslot));
let slot = *next;
*next += 1;
lines.push(format!("${slot} = arith sub ${u_in} ${adj} : Int"));
return Some(slot);
}
let lo_shift = w.bits - k_left;
let hf = emit_pow2_factor(k_left, lines, next)?;
let lf = emit_pow2_factor(lo_shift, lines, next)?;
let hi_part = *next;
*next += 1;
lines.push(format!("${hi_part} = arith mul ${u_in} ${hf} : Int"));
let lo_part = *next;
*next += 1;
lines.push(format!("${lo_part} = arith div ${u_in} ${lf} : Int"));
let raw = *next;
*next += 1;
lines.push(format!("${raw} = arith add ${hi_part} ${lo_part} : Int"));
let t3 = *next;
*next += 1;
lines.push(format!("${t3} = arith mod ${raw} ${} : Int", w.mslot));
let t4 = *next;
*next += 1;
lines.push(format!("${t4} = arith add ${t3} ${} : Int", w.mslot));
let u = *next;
*next += 1;
lines.push(format!("${u} = arith mod ${t4} ${} : Int", w.mslot));
if !w.signed {
return Some(u);
}
let his = *next;
*next += 1;
lines.push(format!("${his} = const {} : Int", w.hi));
let gt = *next;
*next += 1;
lines.push(format!("${gt} = cmp gt ${u} ${his} : Bool"));
let adj = *next;
*next += 1;
lines.push(format!("${adj} = arith mul ${gt} ${} : Int", w.mslot));
let slot = *next;
*next += 1;
lines.push(format!("${slot} = arith sub ${u} ${adj} : Int"));
Some(slot)
}
pub(super) fn emit_synthetic_2_64(lines: &mut Vec<String>, next: &mut usize) -> usize {
let half = *next;
*next += 1;
lines.push(format!("${half} = const 4294967296 : Int"));
let two64 = *next;
*next += 1;
lines.push(format!("${two64} = arith mul ${half} ${half} : Int"));
two64
}
pub(super) fn emit_signed_modulus_slot(
modulus_i64: Option<i64>,
lines: &mut Vec<String>,
next: &mut usize,
) -> usize {
if let Some(modulus) = modulus_i64 {
let m = *next;
*next += 1;
lines.push(format!("${m} = const {modulus} : Int"));
m
} else {
emit_synthetic_2_64(lines, next)
}
}
pub(super) fn emit_u64_max(lines: &mut Vec<String>, next: &mut usize) -> usize {
let two64 = emit_synthetic_2_64(lines, next);
let one = *next;
*next += 1;
lines.push(format!("${one} = const 1 : Int"));
let slot = *next;
*next += 1;
lines.push(format!("${slot} = arith sub ${two64} ${one} : Int"));
slot
}
pub(super) fn emit_pow2_factor(e: u32, lines: &mut Vec<String>, next: &mut usize) -> Option<usize> {
if e > 63 {
return None;
}
if e < 63 {
let factor = 1i64 << e;
let f = *next;
*next += 1;
lines.push(format!("${f} = const {factor} : Int"));
return Some(f);
}
let half = *next;
*next += 1;
lines.push(format!("${half} = const 4294967296 : Int"));
let q = *next;
*next += 1;
lines.push(format!("${q} = const 2147483648 : Int"));
let f = *next;
*next += 1;
lines.push(format!("${f} = arith mul ${half} ${q} : Int"));
Some(f)
}
pub(super) fn pot_exponents(lo: i64, hi: i64) -> Option<u32> {
if is_u64_width_bounds(lo, hi) {
return Some(64);
}
if lo == 0 {
let m = (hi as u64).checked_add(1)?;
if !m.is_power_of_two() {
return None;
}
Some(m.trailing_zeros())
} else if lo < 0 && hi > 0 {
let half = (hi as u64).checked_add(1)?;
if !half.is_power_of_two() {
return None;
}
Some(half.trailing_zeros())
} else {
None
}
}
pub(super) fn path_param_bounds(expr: &syn::Expr) -> Option<(i64, i64)> {
let name = match expr {
syn::Expr::Paren(p) => return path_param_bounds(&p.expr),
syn::Expr::Group(g) => return path_param_bounds(&g.expr),
syn::Expr::Reference(r) => return path_param_bounds(&r.expr),
syn::Expr::Unary(u) if matches!(u.op, syn::UnOp::Deref(_)) => {
return path_param_bounds(&u.expr);
}
syn::Expr::MethodCall(m)
if m.args.is_empty()
&& (is_identity_peel_method(&m.method.to_string())
|| m.method == "get") =>
{
return path_param_bounds(&m.receiver);
}
syn::Expr::Path(p) if p.path.segments.len() == 1 => p.path.segments[0].ident.to_string(),
_ => return None,
};
PARAM_BOUNDS.with(|c| c.borrow().get(&name).copied())
}
pub(super) fn wrap_bounds_for(receiver: &syn::Expr) -> Option<(i64, i64)> {
SAT_BOUNDS
.get()
.or_else(|| path_param_bounds(receiver))
.or_else(|| expr_int_bounds(receiver))
}
pub(super) fn expr_int_bounds(expr: &syn::Expr) -> Option<(i64, i64)> {
if let Some(b) = path_param_bounds(expr) {
return Some(b);
}
match expr {
syn::Expr::Paren(p) => expr_int_bounds(&p.expr),
syn::Expr::Group(g) => expr_int_bounds(&g.expr),
syn::Expr::Reference(r) => expr_int_bounds(&r.expr),
syn::Expr::Unary(u) => expr_int_bounds(&u.expr),
syn::Expr::Binary(b) => expr_int_bounds(&b.left).or_else(|| expr_int_bounds(&b.right)),
syn::Expr::MethodCall(m) if m.args.is_empty() => expr_int_bounds(&m.receiver),
syn::Expr::MethodCall(m) if m.args.len() == 1 => {
expr_int_bounds(&m.receiver).or_else(|| expr_int_bounds(&m.args[0]))
}
_ => None,
}
}
pub(super) fn lit_int_i64_bits(expr: &syn::Expr) -> Option<(i64, u32)> {
lit_int_i64_bits_signed(expr).map(|(v, bits, _)| (v, bits))
}
pub(super) fn lit_int_i64_bits_signed(expr: &syn::Expr) -> Option<(i64, u32, bool)> {
match expr {
syn::Expr::Paren(p) => lit_int_i64_bits_signed(&p.expr),
syn::Expr::Group(g) => lit_int_i64_bits_signed(&g.expr),
syn::Expr::Unary(u) if matches!(u.op, syn::UnOp::Neg(_)) => {
let (v, bits, signed) = lit_int_i64_bits_signed(&u.expr)?;
Some((v.checked_neg()?, bits, signed))
}
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(n),
..
}) => {
let (bits, signed) = match n.suffix() {
"u8" => (8, false),
"i8" => (8, true),
"u16" => (16, false),
"i16" => (16, true),
"u32" => (32, false),
"i32" => (32, true),
"u64" | "usize" => (64, false),
"i64" | "isize" => (64, true),
_ => return None,
};
let v: i64 = n.base10_parse().ok()?;
Some((v, bits, signed))
}
_ => None,
}
}