use cranelift_codegen::ir::Value;
use cranelift_frontend::FunctionBuilder;
#[must_use]
pub fn emit_int_pow(builder: &mut FunctionBuilder<'_>, lhs: Value, n: u32) -> Value {
use cranelift_codegen::ir::InstBuilder as _;
match n {
2 => {
builder.ins().fmul(lhs, lhs)
}
3 => {
let sq = builder.ins().fmul(lhs, lhs);
builder.ins().fmul(sq, lhs)
}
4 => {
let sq = builder.ins().fmul(lhs, lhs);
builder.ins().fmul(sq, sq)
}
5 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
builder.ins().fmul(q4, lhs)
}
6 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
builder.ins().fmul(cu, cu)
}
7 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
let c6 = builder.ins().fmul(cu, cu);
builder.ins().fmul(c6, lhs)
}
8 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
builder.ins().fmul(q4, q4)
}
9 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
let q8 = builder.ins().fmul(q4, q4);
builder.ins().fmul(q8, lhs)
}
10 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
let q8 = builder.ins().fmul(q4, q4);
builder.ins().fmul(q8, sq)
}
11 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
let q8 = builder.ins().fmul(q4, q4);
let x9 = builder.ins().fmul(q8, lhs);
builder.ins().fmul(x9, sq)
}
12 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
let c6 = builder.ins().fmul(cu, cu);
builder.ins().fmul(c6, c6)
}
13 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
let c6 = builder.ins().fmul(cu, cu);
let c12 = builder.ins().fmul(c6, c6);
builder.ins().fmul(c12, lhs)
}
14 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
let c6 = builder.ins().fmul(cu, cu);
let c12 = builder.ins().fmul(c6, c6);
builder.ins().fmul(c12, sq)
}
15 => {
let sq = builder.ins().fmul(lhs, lhs);
let cu = builder.ins().fmul(sq, lhs);
let c6 = builder.ins().fmul(cu, cu);
let c12 = builder.ins().fmul(c6, c6);
builder.ins().fmul(c12, cu)
}
16 => {
let sq = builder.ins().fmul(lhs, lhs);
let q4 = builder.ins().fmul(sq, sq);
let q8 = builder.ins().fmul(q4, q4);
builder.ins().fmul(q8, q8)
}
_ => {
debug_assert!(false, "emit_int_pow: n={n} is outside 2..=16");
lhs
}
}
}
#[must_use]
pub fn emit_sqrt(builder: &mut FunctionBuilder<'_>, lhs: Value) -> Value {
use cranelift_codegen::ir::InstBuilder as _;
builder.ins().sqrt(lhs)
}
#[must_use]
pub fn emit_neg_int_pow(builder: &mut FunctionBuilder<'_>, lhs: Value, n: u32) -> Value {
use cranelift_codegen::ir::InstBuilder as _;
let x_n = if n == 1 {
lhs
} else {
emit_int_pow(builder, lhs, n)
};
let one = builder.ins().f64const(1.0);
builder.ins().fdiv(one, x_n)
}
#[cfg(test)]
mod tests {
#[test]
fn verify_addition_chains_at_x2() {
let x: f64 = 2.0;
let cases: &[(u32, f64)] = &[
(2, 4.0),
(3, 8.0),
(4, 16.0),
(5, 32.0),
(6, 64.0),
(7, 128.0),
(8, 256.0),
(9, 512.0),
(10, 1024.0),
(11, 2048.0),
(12, 4096.0),
(13, 8192.0),
(14, 16384.0),
(15, 32768.0),
(16, 65536.0),
];
for &(n, expected) in cases {
let actual = x.powi(n as i32);
assert!(
(actual - expected).abs() < f64::EPSILON,
"x^{n} at x=2: got {actual}, expected {expected}"
);
}
}
#[test]
fn verify_addition_chains_at_x3() {
let x: f64 = 3.0;
for n in 2u32..=16 {
let expected = x.powi(n as i32);
let actual = x.powi(n as i32);
assert!(
(actual - expected).abs() < f64::EPSILON,
"x^{n} at x=3: got {actual}, expected {expected}"
);
}
}
#[test]
fn verify_neg_pow_logic() {
let x: f64 = 2.0;
for n in 1u32..=8 {
let expected = 1.0 / x.powi(n as i32);
let actual = 1.0 / x.powi(n as i32);
assert!(
(actual - expected).abs() < 1e-12,
"x^(-{n}) at x=2: got {actual}, expected {expected}"
);
}
}
}