1 #ifndef SIMD_OP_CHECK_H
2 #define SIMD_OP_CHECK_H
28 static constexpr
int max_i32 = 0x7fffffff;
53 const std::vector<ImageParam>
image_params{
in_f32,
in_f64,
in_f16,
in_bf16,
in_i8,
in_u8,
in_i16,
in_u16,
in_i32,
in_u32,
in_i64,
in_u64};
54 const std::vector<Argument>
arg_types{
in_f32,
in_f64,
in_f16,
in_bf16,
in_i8,
in_u8,
in_i16,
in_u16,
in_i32,
in_u32,
in_i64,
in_u64};
79 bool can_run_the_code =
109 can_run_the_code =
false;
112 return can_run_the_code;
115 virtual void compile_and_check(
Func error,
const std::string &op,
const std::string &name,
int vector_width, std::ostringstream &error_msg) {
116 std::string fn_name =
"test_" + name;
120 std::map<OutputFileType, std::string> outputs = {
127 std::ifstream asm_file;
128 asm_file.open(file_name +
".s");
130 bool found_it =
false;
132 std::ostringstream msg;
133 msg << op <<
" did not generate for target=" <<
get_run_target().
to_string() <<
" vector_width=" << vector_width <<
". Instead we got:\n";
136 while (getline(asm_file, line)) {
144 error_msg <<
"Failed: " << msg.str() <<
"\n";
153 while (*p && *str && *p == *str && *p !=
'*') {
160 }
else if (*p ==
'*') {
167 }
else if (*p ==
' ') {
172 }
else if (*str ==
' ') {
198 std::ostringstream error_msg;
206 inline_reduction = f;
210 IRVisitor::visit(op);
216 } has_inline_reduction;
217 e.
accept(&has_inline_reduction);
229 if (has_inline_reduction.result) {
234 Func g{has_inline_reduction.inline_reduction};
241 .split(x, xo, xi, vector_width)
243 .vectorize(g.rvars()[0])
250 error() = Halide::cast<double>(
maximum(
absd(f(r_check.
x, r_check.
y), f_scalar(r_check.
x, r_check.
y))));
256 if (can_run_the_code) {
263 if (!buf.defined())
continue;
269 if (t ==
Float(32)) {
270 buf.as<
float>().for_each_value([&](
float &f) { f = (
rng() & 0xfff) / 8.0f - 0xff; });
271 }
else if (t ==
Float(64)) {
272 buf.as<
double>().for_each_value([&](
double &f) { f = (
rng() & 0xfff) / 8.0 - 0xff; });
273 }
else if (t ==
Float(16)) {
278 ptr != (
uint32_t *)buf.data() + buf.size_in_bytes() / 4;
293 error_msg <<
"The vector and scalar versions of " << name <<
" disagree. Maximum error: " << e <<
"\n";
298 std::ifstream error_file;
299 error_file.open(error_filename);
301 error_msg <<
"Error assembly: \n";
303 while (getline(error_file, line)) {
304 error_msg << line <<
"\n";
311 return {op, error_msg.str()};
316 std::string name =
"op_" + op;
317 for (
size_t i = 0; i < name.size(); i++) {
318 if (!isalnum(name[i])) name[i] =
'_';
321 name +=
"_" + std::to_string(
tasks.size());
328 tasks.emplace_back(
Task{op, name, vector_width, e});
335 const int alignment_bytes = 16;
336 p.set_host_alignment(alignment_bytes);
337 const int alignment = alignment_bytes / p.type().bytes();
338 p.dim(0).set_min((p.dim(0).min() / alignment) * alignment);
347 const std::string run_target_str = run_target.
to_string();
351 for (
size_t t = 0; t <
tasks.size(); t++) {
353 const auto &task =
tasks.at(t);
354 auto result =
check_one(task.op, task.name, task.vector_width, task.expr);
355 constexpr
int tabstop = 32;
356 const int spaces =
std::max(1, tabstop - (
int)result.op.size());
357 std::cout << result.op << std::string(spaces,
' ') <<
"(" << run_target_str <<
")\n";
358 if (!result.error_msg.empty()) {
359 std::cerr << result.error_msg;
367 template<
typename SIMDOpCheckT>
368 static int main(
int argc,
char **argv,
const std::vector<Target> &targets_to_test) {
370 std::cout <<
"host is: " << host <<
"\n";
372 const int seed = argc > 2 ?
atoi(argv[2]) : time(
nullptr);
373 std::cout <<
"simd_op_check test seed: " << seed <<
"\n";
375 for (
const auto &t : targets_to_test) {
376 if (!t.supported()) {
377 std::cout <<
"[SKIP] Unsupported target: " << t <<
"\n";
380 SIMDOpCheckT test(t);
382 if (!t.supported()) {
383 std::cout <<
"Halide was compiled without support for " << t.to_string() <<
". Skipping.\n";
388 test.filter = argv[1];
391 if (
getenv(
"HL_SIMD_OP_CHECK_FILTER")) {
392 test.filter =
getenv(
"HL_SIMD_OP_CHECK_FILTER");
404 test.output_directory = argv[2];
407 bool success = test.test_all();
417 std::cout <<
"Success!\n";
A Halide::Buffer is a named shared reference to a Halide::Runtime::Buffer.
void compile_to_assembly(const std::string &filename, const std::vector< Argument > &, const std::string &fn_name, const Target &target=get_target_from_environment())
Statically compile this function to text assembly equivalent to the object file generated by compile_...
Func & compute_root()
Compute all of this function once ahead of time.
Stage update(int idx=0)
Get a handle on an update step for the purposes of scheduling it.
Func & vectorize(const VarOrRVar &var)
Mark a dimension to be computed all-at-once as a single vector.
Func & compute_at(const Func &f, const Var &var)
Compute this function as needed for each unique value of the given var for the given calling function...
void infer_input_bounds(const std::vector< int32_t > &sizes, const Target &target=get_jit_target_from_environment())
For a given size of output, or a given output buffer, determine the bounds required of all unbound Im...
Func & bound(const Var &var, Expr min, Expr extent)
Statically declare that the range over which a function should be evaluated is given by the second an...
void compile_to(const std::map< OutputFileType, std::string > &output_files, const std::vector< Argument > &args, const std::string &fn_name, const Target &target=get_target_from_environment())
Compile and generate multiple target files with single call.
Realization realize(std::vector< int32_t > sizes={}, const Target &target=Target())
Evaluate this function over some rectangular domain and return the resulting buffer or buffers.
Func clone_in(const Func &f)
Similar to Func::in; however, instead of replacing the call to this Func with an identity Func that r...
An Image parameter to a halide pipeline.
A reference-counted handle to Halide's internal representation of a function.
bool has_update_definition() const
Does this function have an update definition?
A base class for algorithms that need to recursively walk over the IR.
virtual void visit(const IntImm *)
bool should_run(size_t task_index) const
A multi-dimensional domain over which to iterate.
RVar x
Direct access to the first four dimensions of the reduction domain.
A reduction variable represents a single dimension of a reduction domain (RDom).
A Realization is a vector of references to existing Buffer objects.
virtual void compile_and_check(Func error, const std::string &op, const std::string &name, int vector_width, std::ostringstream &error_msg)
static constexpr int max_u8
const std::vector< Argument > arg_types
std::string output_directory
static constexpr int max_i32
virtual void setup_images()
virtual void add_tests()=0
bool wildcard_match(const std::string &p, const std::string &str) const
static constexpr int max_i8
static constexpr int max_u16
bool wildcard_search(const std::string &p, const std::string &str) const
bool wildcard_match(const char *p, const char *str) const
virtual ~SimdOpCheckTest()=default
SimdOpCheckTest(const Target t, int w, int h)
void check(std::string op, int vector_width, Expr e)
Target get_run_target() const
static int main(int argc, char **argv, const std::vector< Target > &targets_to_test)
const std::vector< ImageParam > image_params
static constexpr int max_i16
TestResult check_one(const std::string &op, const std::string &name, int vector_width, Expr e)
virtual bool can_run_code() const
std::vector< Task > tasks
A Halide variable, to be used when defining functions.
std::string get_test_tmp_dir()
Return the path to a directory that can be safely written to when running tests; the contents directo...
std::map< OutputFileType, const OutputInfo > get_output_info(const Target &target)
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Target get_host_target()
Return the target corresponding to the host machine.
Type BFloat(int bits, int lanes=1)
Construct a floating-point type in the bfloat format.
Type UInt(int bits, int lanes=1)
Constructing an unsigned integer type.
Type Float(int bits, int lanes=1)
Construct a floating-point type.
Expr maximum(Expr, const std::string &s="maximum")
Type Int(int bits, int lanes=1)
Constructing a signed integer type.
Expr absd(Expr a, Expr b)
Return the absolute difference between two values.
void compile_standalone_runtime(const std::string &object_filename, const Target &t)
Create an object file containing the Halide runtime for a given target.
Expr max(const FuncRef &a, const FuncRef &b)
char * getenv(const char *)
unsigned __INT32_TYPE__ uint32_t
A fragment of Halide syntax.
@ Halide
A call to a Func.
void accept(IRVisitor *v) const
Dispatch to the correct visitor method for this node.
static bool can_jit_target(const Target &target)
If the given target can be executed via the wasm executor, return true.
A struct representing a target machine and os to generate code for.
enum Halide::Target::Arch arch
bool has_feature(Feature f) const
int bits
The bit-width of the target machine.
enum Halide::Target::OS os
std::string to_string() const
Convert the Target into a string form that can be reconstituted by merge_string(),...
Target without_feature(Feature f) const
Return a copy of the target with the given feature cleared.
Feature
Optional features a target can have.
Target with_feature(Feature f) const
Return a copy of the target with the given feature set.
Types in the halide type system.
Expr max() const
Return an expression which is the maximum value of this type.
Class that provides a type that implements half precision floating point (IEEE754 2008 binary16) in s...