use crate::devices::cpu::{CpuBackend, create_cpu_device_with_backend};
use svod_device::registry::DeviceRegistry;
use svod_dtype::{AddrSpace, DType};
use svod_ir::{ConstValue, UOp};
fn build_copy_sink() -> std::sync::Arc<UOp> {
let ptr_dtype = DType::Float32.ptr(None, AddrSpace::Global);
let out = UOp::param(0, 16, ptr_dtype.clone(), None);
let inp = UOp::param(1, 16, ptr_dtype, None);
let idx = UOp::const_(DType::Index, ConstValue::Int(0));
let out_index = UOp::index().buffer(out).indices(vec![idx.clone()]).call().expect("output INDEX");
let in_index = UOp::index().buffer(inp.clone()).indices(vec![idx]).call().expect("input INDEX");
let load = UOp::load().buffer(inp).index(in_index).call();
let store = out_index.store(load);
UOp::sink(vec![store])
}
#[test]
fn test_cpu_device_creation_llvm() {
let registry = DeviceRegistry::default();
let device =
create_cpu_device_with_backend(®istry, CpuBackend::Llvm).expect("Failed to create CPU device with LLVM");
assert_eq!(device.base_device_key(), "CPU");
assert_eq!(device.compiler.cache_key(), "llvm-jit");
}
#[test]
fn test_compile_and_runtime_pipeline_llvm() {
use svod_device::device::ProgramSpec;
use svod_dtype::DeviceSpec;
let registry = DeviceRegistry::default();
let device = create_cpu_device_with_backend(®istry, CpuBackend::Llvm).unwrap();
let llvm_ir = r#"
define void @test_kernel() {
entry:
ret void
}
"#;
let sink = UOp::sink(vec![]);
let spec = ProgramSpec::new("test_kernel".to_string(), llvm_ir.to_string(), DeviceSpec::Cpu, sink);
let compiled = device.compiler.compile(&spec).expect("Compile should succeed");
assert!(compiled.src.is_some(), "LLVM JIT should have source");
assert!(compiled.bytes.is_empty(), "LLVM JIT should have empty bytes");
assert_eq!(compiled.name, "test_kernel");
let program = (device.runtime)(&compiled).expect("RuntimeFactory should succeed");
assert!(!program.name().is_empty(), "Program should have a name");
let pointers: Vec<*mut u8> = vec![];
unsafe {
program.execute(&pointers, &[], None, None).expect("Execution should succeed");
}
}
#[test]
fn test_compile_invalid_ir() {
use svod_device::device::ProgramSpec;
use svod_dtype::DeviceSpec;
let registry = DeviceRegistry::default();
let device = create_cpu_device_with_backend(®istry, CpuBackend::Llvm).unwrap();
let sink = UOp::sink(vec![]);
let spec = ProgramSpec::new("test".to_string(), "this is not valid LLVM IR".to_string(), DeviceSpec::Cpu, sink);
let result = device.compiler.compile(&spec);
assert!(result.is_ok(), "Should return CompiledSpec even with invalid IR (validation TODO)");
}
#[test]
fn test_renderer_metadata_consistent_between_clang_and_llvm() {
let registry = DeviceRegistry::default();
let clang = create_cpu_device_with_backend(®istry, CpuBackend::Clang).expect("create clang device");
let llvm = create_cpu_device_with_backend(®istry, CpuBackend::Llvm).expect("create llvm device");
let sink = build_copy_sink();
let linear = UOp::linear(sink.toposort().into());
let clang_spec = clang.renderer.render(&linear, Some("meta_copy")).expect("clang render");
let llvm_spec = llvm.renderer.render(&linear, Some("meta_copy")).expect("llvm render");
assert_eq!(clang_spec.globals, vec![0, 1]);
assert_eq!(clang_spec.outs, vec![0]);
assert_eq!(clang_spec.ins, vec![1]);
assert_eq!(clang_spec.globals, llvm_spec.globals);
assert_eq!(clang_spec.outs, llvm_spec.outs);
assert_eq!(clang_spec.ins, llvm_spec.ins);
}