use karpal_verify::gpu::GpuObligationBundle;
use karpal_verify::{ObligationBundle, Origin};
pub use karpal_proof::{Property, Proven};
pub use karpal_verify::gpu::{
IsBufferAlignedTo16, IsDispatchWithinLimits, IsMSLKernelDeterministic,
IsWorkgroupSizeDivisible,
};
pub fn add_one_obligations() -> ObligationBundle {
GpuObligationBundle::metal_kernel(
"borsalino_add_one",
Origin::new("borsalino", "kernels::add_one"),
)
.with_buffer_alignment("input_buffer", 16)
.with_buffer_alignment("output_buffer", 16)
.with_workgroup_divisibility("thread_count", 256)
.with_dispatch_limit("workgroup_count", 65_535)
.with_kernel_determinism("add_one_kernel")
.into_bundle()
}
pub fn scale_obligations() -> ObligationBundle {
GpuObligationBundle::metal_kernel(
"borsalino_scale",
Origin::new("borsalino", "kernels::scale"),
)
.with_buffer_alignment("input_buffer", 16)
.with_buffer_alignment("output_buffer", 16)
.with_workgroup_divisibility("thread_count", 256)
.with_dispatch_limit("workgroup_count", 65_535)
.with_kernel_determinism("scale_kernel")
.into_bundle()
}
pub fn saxpy_obligations() -> ObligationBundle {
GpuObligationBundle::metal_kernel(
"borsalino_saxpy",
Origin::new("borsalino", "kernels::saxpy"),
)
.with_buffer_alignment("x_buffer", 16)
.with_buffer_alignment("y_buffer", 16)
.with_buffer_alignment("out_buffer", 16)
.with_workgroup_divisibility("thread_count", 256)
.with_dispatch_limit("workgroup_count", 65_535)
.with_kernel_determinism("saxpy_kernel")
.into_bundle()
}
#[cfg(test)]
mod tests {
use super::*;
use karpal_verify::{export_kani_bundle, export_lean_bundle, export_smt_bundle};
#[test]
fn add_one_bundle_contains_all_properties() {
let bundle = add_one_obligations();
let obligations = bundle.obligations();
assert!(
obligations
.iter()
.any(|o| o.property == IsBufferAlignedTo16::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsWorkgroupSizeDivisible::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsDispatchWithinLimits::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsMSLKernelDeterministic::NAME)
);
}
#[test]
fn add_one_bundle_exports_through_all_backends() {
let bundle = add_one_obligations();
let smt = export_smt_bundle(&bundle);
let lean = export_lean_bundle("BorsalinoVerify", &bundle);
let kani = export_kani_bundle(&bundle);
assert!(!smt.is_empty(), "SMT export should not be empty");
assert!(
lean.contains("deterministic_kernel"),
"Lean export should contain deterministic_kernel"
);
assert!(!kani.is_empty(), "Kani export should not be empty");
assert!(
kani.iter().any(|h| h.source.contains("kani::assert")),
"Kani harness should contain assertions"
);
}
#[test]
fn scale_bundle_contains_all_properties() {
let bundle = scale_obligations();
let obligations = bundle.obligations();
assert!(
obligations
.iter()
.any(|o| o.property == IsBufferAlignedTo16::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsWorkgroupSizeDivisible::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsDispatchWithinLimits::NAME)
);
assert!(
obligations
.iter()
.any(|o| o.property == IsMSLKernelDeterministic::NAME)
);
}
#[test]
fn saxpy_bundle_contains_three_buffer_alignments() {
let bundle = saxpy_obligations();
let obligations = bundle.obligations();
let alignment_count = obligations
.iter()
.filter(|o| o.property == IsBufferAlignedTo16::NAME)
.count();
assert_eq!(
alignment_count, 3,
"saxpy should have 3 buffer alignment obligations (x, y, out)"
);
}
}