A toy halide clone.
Not a lot is implemented yet, but it is possible to JIT and run a basic image pipeline. The details are in flux, but as of the time of writing the following example works (and produces terrible code).
// Define the pipeline
let (x, y) = (Var::X, Var::Y);
source!(input);
func!(blur_h = (input.at(x - 1, y) + input.at(x, y) + input.at(x + 1, y)) / 3);
func!(blur_v = (blur_h.at(x, y - 1) + blur_h.at(x, y) + blur_h.at(x, y + 1)) / 3);
let graph = Graph::new("blur3x3", vec![blur_h, blur_v]);
// Generate LLVM IR
let module = create_optimised_module(context, &graph);
// Generate native code
let engine = ExecutionEngine::new(module);
let processor = engine.get_processor(&graph);
// Run the generated code
let inputs = [(&input, &example_image(20, 10))];
let results = processor.process(&inputs);
See examples/jit.rs for the latest runnable version of this.
To run the examples you'll need to have an appropriate version of LLVM installed and to provide rustc with a path to it. In my case I brew installed llvm and added /usr/local/opt/llvm/bin to my path.
This library also defines some basic functionality for tracing image processing operations, although these aren't yet integrated with the JIT functionality. The following examples were generated by examples/trace.rs, which runs the handwritten blur functions from src/blur3.rs and uses the TraceImage
implementation of the Image
trait to generate replay visualisations.
The goal is to add support for generating (and tracing) these schedules and more automatically. But I've not even started on that yet.
Dimensions: y, x. Compute at blur_v.x, store at blur_v.x
Dimensions: y, x. Compute at root, store at root
Dimensions: y, x. Compute at blur_v.x, store at root
Dimensions: yo, y, x. Compute at blur_v.yo, store at blur_v.yo
Dimension: yo, xo, y, x. Compute at blur_v.xo, store at blur_v.xo