#[derive(Debug, Clone)]
pub struct PtxTarget {
pub version: (u32, u32),
pub sm: String,
}
#[must_use]
pub fn ptx_target_for_cc(major: i32, minor: i32) -> PtxTarget {
let sm = format!("sm_{major}{minor}");
let version = match (major, minor) {
(7, 5) => (7, 4),
(8, 0) | (8, 6) => (7, 5),
(8, 9) => (8, 0),
(9, 0 | 1) => (8, 0),
(10, _) => (8, 5),
(12, _) => (8, 7),
_ => (7, 4),
};
PtxTarget { version, sm }
}
#[must_use]
pub fn ptx_header(target: &PtxTarget) -> String {
format!(
".version {}.{}\n.target {}\n.address_size 64\n",
target.version.0, target.version.1, target.sm
)
}
#[must_use]
pub fn f64_imm(v: f64) -> String {
format!("0d{:016X}", v.to_bits())
}
#[must_use]
pub fn horner_f64(coeffs: &[f64], x_reg: u32, out_reg: u32) -> String {
debug_assert!(
!coeffs.is_empty(),
"horner_f64 needs at least one coefficient"
);
let mut s = String::new();
let last = coeffs.len() - 1;
s.push_str(&format!(
"\tmov.f64 \t%fd{out_reg}, {};\n",
f64_imm(coeffs[last])
));
for k in (0..last).rev() {
s.push_str(&format!(
"\tfma.rn.f64 \t%fd{out_reg}, %fd{out_reg}, %fd{x_reg}, {};\n",
f64_imm(coeffs[k])
));
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f64_imm_known_patterns() {
assert_eq!(f64_imm(1.0), "0d3FF0000000000000");
assert_eq!(f64_imm(2.0), "0d4000000000000000");
assert_eq!(f64_imm(0.0), "0d0000000000000000");
}
#[test]
fn ptx_target_ampere() {
let t = ptx_target_for_cc(8, 6);
assert_eq!(t.sm, "sm_86");
assert_eq!(t.version, (7, 5));
}
#[test]
fn horner_emits_fma_chain() {
let ptx = horner_f64(&[1.0, 2.0, 3.0], 0, 1);
assert_eq!(ptx.matches("fma.rn.f64").count(), 2);
assert_eq!(ptx.matches("mov.f64").count(), 1);
}
}