use std::path::Path;
use provable_contracts::latex::{latex_escape, math_to_latex};
use provable_contracts::schema::{Contract, Equation, parse_contract};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Text,
Latex,
Ptx,
Asm,
}
impl OutputFormat {
pub fn from_str(s: &str) -> Result<Self, String> {
match s {
"text" => Ok(Self::Text),
"latex" => Ok(Self::Latex),
"ptx" => Ok(Self::Ptx),
"asm" => Ok(Self::Asm),
other => Err(format!(
"unknown format '{other}', expected 'text', 'latex', 'ptx', or 'asm'"
)),
}
}
}
pub fn run(path: &Path, format: OutputFormat) -> Result<(), Box<dyn std::error::Error>> {
let contract = parse_contract(path)?;
let name = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown");
match format {
OutputFormat::Text => render_text(name, &contract.equations),
OutputFormat::Latex => render_latex(name, &contract.equations),
OutputFormat::Ptx => render_ptx(name, &contract),
OutputFormat::Asm => render_asm(name, &contract),
}
Ok(())
}
fn render_text(name: &str, equations: &std::collections::BTreeMap<String, Equation>) {
println!("Equations for {name}");
println!("{}", "=".repeat(40 + name.len()));
println!();
for (id, eq) in equations {
println!(" {id}");
println!(" formula: {}", eq.formula);
if let Some(ref dom) = eq.domain {
println!(" domain: {dom}");
}
if let Some(ref cod) = eq.codomain {
println!(" codomain: {cod}");
}
if !eq.invariants.is_empty() {
println!(" invariants:");
for inv in &eq.invariants {
println!(" - {inv}");
}
}
println!();
}
}
fn render_latex(name: &str, equations: &std::collections::BTreeMap<String, Equation>) {
let escaped_name = latex_escape(name);
println!("% Equations for {name}");
println!("\\section{{Equations: {escaped_name}}}");
println!();
for (id, eq) in equations {
let escaped_id = latex_escape(id);
let latex_formula = math_to_latex(&eq.formula);
println!("\\subsection{{{escaped_id}}}");
println!("\\label{{eq:{id}}}");
println!();
println!("\\begin{{equation}}");
println!(" {latex_formula}");
println!("\\end{{equation}}");
println!();
if let Some(ref dom) = eq.domain {
println!("\\textbf{{Domain:}} ${}$", math_to_latex(dom));
println!();
}
if let Some(ref cod) = eq.codomain {
println!("\\textbf{{Codomain:}} ${}$", math_to_latex(cod));
println!();
}
if !eq.invariants.is_empty() {
println!("\\textbf{{Invariants:}}");
println!("\\begin{{itemize}}");
for inv in &eq.invariants {
println!(" \\item ${}$", math_to_latex(inv));
}
println!("\\end{{itemize}}");
println!();
}
}
}
fn render_ptx(name: &str, contract: &Contract) {
let kernel = kernel_name(name);
render_header_comment("PTX kernel stub", name, contract);
println!(".version 8.5");
println!(".target sm_90");
println!(".address_size 64");
println!();
println!(".visible .entry {kernel}(");
println!(" .param .u64 input,");
println!(" .param .u64 output,");
println!(" .param .u32 n");
println!(")");
println!("{{");
println!(" .reg .u32 %tid, %n;");
println!(" .reg .u64 %in_ptr, %out_ptr;");
println!(" .reg .f32 %val, %acc;");
println!();
println!(" ld.param.u64 %in_ptr, [input];");
println!(" ld.param.u64 %out_ptr, [output];");
println!(" ld.param.u32 %n, [n];");
println!();
println!(" mov.u32 %tid, %ctaid.x;");
println!(" mad.lo.u32 %tid, %tid, %ntid.x, %tid.x;");
println!();
render_body_comments(contract);
println!(" ret;");
println!("}}");
}
fn render_asm(name: &str, contract: &Contract) {
let kernel = kernel_name(name);
let isa = detect_simd_isa(contract);
render_header_comment(&format!("x86-64 {} stub", isa.label), name, contract);
println!(".intel_syntax noprefix");
println!(".text");
println!(".globl {kernel}_{}", isa.suffix);
println!(".p2align 4");
println!();
println!("{kernel}_{}:", isa.suffix);
println!(" push rbp");
println!(" mov rbp, rsp");
println!(" // rdi = input ptr, rsi = output ptr, edx = n");
println!();
if isa.width > 0 {
let r = isa.reg_prefix;
println!(" // {} registers: {r} x {}", isa.label, isa.reg_count);
for i in 0..std::cmp::min(isa.reg_count, 4) {
println!(" vxorps {r}{i}, {r}{i}, {r}{i}");
}
println!();
}
render_body_comments(contract);
println!(" pop rbp");
println!(" ret");
}
fn render_header_comment(label: &str, name: &str, contract: &Contract) {
println!("//");
println!("// {label}: {name}");
println!("// {}", contract.metadata.description);
for (id, eq) in &contract.equations {
println!("// Equation {id}: {}", eq.formula);
}
println!("//");
println!();
}
fn render_body_comments(contract: &Contract) {
if let Some(ref ks) = contract.kernel_structure {
for (i, phase) in ks.phases.iter().enumerate() {
println!(" // Phase {}: {}", i + 1, phase.name);
println!(" // {}", phase.description);
if let Some(ref inv) = phase.invariant {
println!(" // Invariant: {inv}");
}
println!();
}
} else {
for (id, eq) in &contract.equations {
println!(" // Equation: {id}");
println!(" // {}", eq.formula);
println!();
}
}
if !contract.proof_obligations.is_empty() {
println!(" // Proof obligations:");
for ob in &contract.proof_obligations {
println!(" // [{}] {}", ob.obligation_type, ob.property);
}
println!();
}
}
struct SimdIsa {
label: &'static str,
suffix: &'static str,
reg_prefix: &'static str,
reg_count: u32,
width: u32,
}
fn detect_simd_isa(contract: &Contract) -> SimdIsa {
let has = |pat: &str| {
contract
.simd_dispatch
.values()
.any(|m| m.keys().any(|k| k.contains(pat)))
};
if has("avx512") || has("512") {
SimdIsa {
label: "AVX-512",
suffix: "avx512",
reg_prefix: "zmm",
reg_count: 32,
width: 512,
}
} else if has("avx2") {
SimdIsa {
label: "AVX2",
suffix: "avx2",
reg_prefix: "ymm",
reg_count: 16,
width: 256,
}
} else if !contract.simd_dispatch.is_empty() {
SimdIsa {
label: "SSE4.1",
suffix: "sse41",
reg_prefix: "xmm",
reg_count: 16,
width: 128,
}
} else {
SimdIsa {
label: "scalar",
suffix: "scalar",
reg_prefix: "xmm",
reg_count: 0,
width: 0,
}
}
}
fn kernel_name(contract_name: &str) -> String {
let mut s = contract_name.to_string();
if let Some(pos) = s.rfind("-v") {
if s[pos + 2..].chars().all(|c| c.is_ascii_digit()) {
s.truncate(pos);
}
}
if let Some(stripped) = s.strip_suffix("-kernel") {
s = stripped.to_string();
}
s.replace('-', "_")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_output_format_from_str() {
assert_eq!(OutputFormat::from_str("text").unwrap(), OutputFormat::Text);
assert_eq!(
OutputFormat::from_str("latex").unwrap(),
OutputFormat::Latex
);
assert_eq!(OutputFormat::from_str("ptx").unwrap(), OutputFormat::Ptx);
assert_eq!(OutputFormat::from_str("asm").unwrap(), OutputFormat::Asm);
assert!(OutputFormat::from_str("json").is_err());
}
#[test]
fn test_from_str_other_format_returns_descriptive_error() {
let other = "csv";
let err = OutputFormat::from_str(other).unwrap_err();
assert!(
err.contains(other),
"error should include the unrecognized format"
);
assert!(err.contains("unknown format"));
}
#[test]
fn test_kernel_name() {
assert_eq!(kernel_name("softmax-kernel-v1"), "softmax");
assert_eq!(kernel_name("rmsnorm-kernel-v1"), "rmsnorm");
assert_eq!(kernel_name("flash-attention-v1"), "flash_attention");
assert_eq!(
kernel_name("model-config-algebra-v1"),
"model_config_algebra"
);
assert_eq!(kernel_name("silu-kernel-v2"), "silu");
}
#[test]
fn test_detect_simd_isa_avx2() {
use provable_contracts::schema::parse_contract_str;
let yaml = "metadata:\n version: '1.0'\n description: test\n\
equations:\n eq1:\n formula: 'y = x'\n\
simd_dispatch:\n k:\n scalar: s\n avx2: a\n";
let contract = parse_contract_str(yaml).unwrap();
let isa = detect_simd_isa(&contract);
assert_eq!(isa.suffix, "avx2");
assert_eq!(isa.width, 256);
}
}