#![cfg(feature = "kernel-registry")]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tensor_wasm_core::types::TenantId;
use tensor_wasm_jit::cache::{CacheKey, KernelCache, KernelCacheConfig};
use tensor_wasm_jit::registry::{
sign_manifest, InMemoryBlueprintResolver, InMemoryRegistry, KernelManifest, KernelRegistry,
RegistryError,
};
fn signed_manifest(
name: &str,
version: &str,
sm_version: u32,
ptx_text: &str,
key: &[u8; 32],
) -> KernelManifest {
let digest = *blake3::hash(ptx_text.as_bytes()).as_bytes();
let mut m = KernelManifest::new(
name.to_string(),
version.to_string(),
sm_version,
digest,
[0u8; 32],
1_700_000_000_000,
"tenant-42".to_string(),
);
m.signature = sign_manifest(&m, key);
m
}
struct PanicOnSecondGetRegistry {
inner: InMemoryRegistry,
calls: AtomicUsize,
}
impl PanicOnSecondGetRegistry {
fn new(hmac_key: [u8; 32]) -> Self {
Self {
inner: InMemoryRegistry::new(hmac_key),
calls: AtomicUsize::new(0),
}
}
fn publish(&self, manifest: KernelManifest, ptx_text: String) -> Result<(), RegistryError> {
self.inner.publish(manifest, ptx_text)
}
}
impl KernelRegistry for PanicOnSecondGetRegistry {
fn get(
&self,
name: &str,
version: &str,
) -> Result<Arc<(KernelManifest, String)>, RegistryError> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
assert!(
n < 1,
"registry.get called more than once: L1 promote must have failed"
);
self.inner.get(name, version)
}
fn publish(&self, manifest: KernelManifest, ptx_text: String) -> Result<(), RegistryError> {
self.inner.publish(manifest, ptx_text)
}
fn list(&self) -> Vec<KernelManifest> {
self.inner.list()
}
}
#[test]
fn registry_hit_promotes_to_l1() {
let key = [0xa5u8; 32];
let ptx = "// .version 8.0\n// .target sm_80\n// matmul.f32\n".to_string();
let manifest = signed_manifest("matmul.f32", "1.0.0", 80, &ptx, &key);
let registry = Arc::new(PanicOnSecondGetRegistry::new(key));
registry
.publish(manifest.clone(), ptx.clone())
.expect("publish ok");
let blueprint_fp: u64 = 0xDEAD_BEEF_CAFE_F00D;
let sm_version: u32 = 80;
let mut resolver = InMemoryBlueprintResolver::new();
resolver.insert(
blueprint_fp,
sm_version,
"matmul.f32".to_string(),
"1.0.0".to_string(),
);
let cache = KernelCache::with_config(
KernelCacheConfig::default().with_registry(registry.clone() as Arc<dyn KernelRegistry>),
);
let cache_key = CacheKey::for_tenant(TenantId(7), blueprint_fp, sm_version);
let first = cache
.get_with_registry_fallback(&cache_key, &resolver)
.expect("registry hit");
assert_eq!(first.ptx.text, ptx);
let second = cache
.get_with_registry_fallback(&cache_key, &resolver)
.expect("L1 hit");
assert_eq!(second.ptx.text, ptx);
}
#[test]
fn registry_miss_returns_none() {
let key = [0xa5u8; 32];
let registry: Arc<dyn KernelRegistry> = Arc::new(InMemoryRegistry::new(key));
let resolver = InMemoryBlueprintResolver::new();
let cache =
KernelCache::with_config(KernelCacheConfig::default().with_registry(registry.clone()));
let cache_key = CacheKey::for_tenant(TenantId(7), 0xCAFEBABE_DEADBEEF, 80);
assert!(cache
.get_with_registry_fallback(&cache_key, &resolver)
.is_none());
let mut resolver2 = InMemoryBlueprintResolver::new();
resolver2.insert(
0xCAFEBABE_DEADBEEF,
80,
"no-such-kernel".to_string(),
"9.9.9".to_string(),
);
assert!(cache
.get_with_registry_fallback(&cache_key, &resolver2)
.is_none());
}
#[test]
fn registry_signature_verified_at_publish_not_resolve() {
let key = [0xa5u8; 32];
let reg = InMemoryRegistry::new(key);
let ptx = "// fake ptx\n".to_string();
let m = signed_manifest("matmul.f32", "1.0.0", 80, &ptx, &key);
reg.publish(m.clone(), ptx.clone())
.expect("valid publish accepted");
let got = reg.get("matmul.f32", "1.0.0").expect("get hit");
assert_eq!(got.0.name, "matmul.f32");
assert_eq!(got.1, ptx);
let mut tampered = signed_manifest("evil", "1.0.0", 80, &ptx, &key);
tampered.signature[0] ^= 0xff;
match reg.publish(tampered, ptx) {
Err(RegistryError::BadSignature(_)) => (),
other => panic!("publish must reject bad signature, got {other:?}"),
}
}
#[test]
fn no_registry_attached_returns_none_even_with_resolver_hit() {
let mut resolver = InMemoryBlueprintResolver::new();
resolver.insert(42, 80, "matmul.f32".to_string(), "1.0.0".to_string());
let cache = KernelCache::new();
let cache_key = CacheKey::for_tenant(TenantId(7), 42, 80);
assert!(cache
.get_with_registry_fallback(&cache_key, &resolver)
.is_none());
}
#[test]
fn registry_via_trait_object() {
let key = [0u8; 32];
let reg = InMemoryRegistry::new(key);
let ptx = "// trait-object kernel\n".to_string();
let m = signed_manifest("attention.bf16", "0.2.1", 80, &ptx, &key);
reg.publish(m, ptx.clone()).unwrap();
let reg_arc: Arc<dyn KernelRegistry> = Arc::new(reg);
let mut resolver = InMemoryBlueprintResolver::new();
resolver.insert(7, 80, "attention.bf16".to_string(), "0.2.1".to_string());
let cache = KernelCache::with_config(KernelCacheConfig::default().with_registry(reg_arc));
let cache_key = CacheKey::for_tenant(TenantId(1), 7, 80);
let got = cache
.get_with_registry_fallback(&cache_key, &resolver)
.expect("resolved");
assert_eq!(got.ptx.text, ptx);
}