#include "Halide.h"
// By convention, Generators always go in a .cpp file, usually with no
// corresponding .h file. They can be enclosed in any C++ namespaces
// you like, but the anonymous namespace is often the best choice.
//
// It's normally considered Best Practice to have exactly one Generator
// per .cpp file, and to have the .cpp file name match the generator name
// with a "_generator" suffix (e.g., Generator with name "foo" should
// live in "foo_generator.cpp"), as it tends to simplify build rules,
// but neither of these are required.
namespace {
enum SomeEnum { Foo, Bar };
// Note the inheritance using the Curiously Recurring Template Pattern
class Example : public Halide::Generator<Example> {
public:
// GeneratorParams, ScheduleParams, Inputs, and Outputs are (by convention)
// always public and always declared at the top of the Generator,
// in the order
// GeneratorParam(s)
// ScheduleParam(s)
// Input(s)
// Output(s)
//
// Note that the Inputs will appear in the C function
// call in the order they are declared. (GeneratorParams and ScheduleParams
// are always referenced by name, not position, so their order is irrelevant.)
//
// All Input variants declared as Generator members must have explicit
// names, and all such names must match the regex [A-Za-z_][A-Za-z_0-9]*
// (i.e., essentially a C/C++ variable name). By convention, the name should
// match the member-variable name.
// GeneratorParams can be float or ints: {default} or {default, min, max}
// (Note that if you want to specify min and max, you must specify both.)
GeneratorParam<float> compiletime_factor{ "compiletime_factor", 1, 0, 100 };
GeneratorParam<int> channels{ "channels", 3 };
// ...or enums: {default, name->value map}
GeneratorParam<SomeEnum> enummy{ "enummy",
Foo,
{ { "foo", Foo },
{ "bar", Bar } } };
// ...or bools: {default}
ScheduleParam<bool> vectorize{ "vectorize", true };
// These are bad names that will produce errors at build time:
// GeneratorParam<bool> badname{ " flag", true };
// GeneratorParam<bool> badname{ "flag ", true };
// GeneratorParam<bool> badname{ "0flag ", true };
// GeneratorParam<bool> badname{ "", true };
// GeneratorParam<bool> badname{ "\001", true };
// GeneratorParam<bool> badname{ "a name? with! stuff*", true };
// Note that a leading underscore is legal-but-reserved in C,
// but it's outright forbidden here. (underscore after first char is ok.)
// GeneratorParam<bool> badname{ "_flag", true };
// We also forbid two underscores in a row.
// GeneratorParam<bool> badname{ "f__lag", true };
// Input<> are arguments passed to the filter when
// it is executed (as opposed to the Generator, during compilation).
// When jitting, there is effectively little difference between the
// two (at least for scalar values). Note that we set a default value of
// 1.0 so that invocations that don't set it explicitly use a predictable value.
Input<float> runtime_factor{ "runtime_factor", 1.0 };
Output<Func> output{ "output", Int(32), 3 };
void generate() {
Func f;
f(x, y) = max(x, y);
output(x, y, c) = cast(output.type(), f(x, y) * c * compiletime_factor * runtime_factor);
}
void schedule() {
output
.bound(c, 0, channels)
.reorder(c, x, y)
.unroll(c);
// Note that we can use the Generator method natural_vector_size()
// here; this produces the width of the SIMD vector being targeted
// divided by the width of the data type.
output
.specialize(vectorize)
.vectorize(x, natural_vector_size(output.type()));
}
private:
Var x{"x"}, y{"y"}, c{"c"};
};
} // namespace
HALIDE_REGISTER_GENERATOR(Example, "example")