Halide 22.0.0 Halide compiler and libraries

Loading...
Searching...
No Matches
Func.h
Go to the documentation of this file.
1#ifndef HALIDE_FUNC_H
2#define HALIDE_FUNC_H
3
4/** \file
5 *
6 * Defines Func - the front-end handle on a halide function, and related classes.
7 */
8
9#include "Argument.h"
10#include "Expr.h"
11#include "JITModule.h"
12#include "Module.h"
13#include "Param.h"
14#include "Pipeline.h"
15#include "RDom.h"
16#include "Target.h"
17#include "Tuple.h"
18#include "Var.h"
19
20#include <map>
21#include <utility>
22
23 namespace Halide {
24
25 class OutputImageParam;
26
27/** A class that can represent Vars or RVars. Used for reorder calls
28 * which can accept a mix of either. */
29 struct VarOrRVar {
30 VarOrRVar(const std::string &n, bool r)
31 : var(n), rvar(n), is_rvar(r) {
32 }
33 VarOrRVar(const Var &v)
34 : var(v), is_rvar(false) {
35 }
36 VarOrRVar(const RVar &r)
37 : rvar(r), is_rvar(true) {
38 }
39 VarOrRVar(const RDom &r)
40 : rvar(RVar(r)), is_rvar(true) {
41 }
42 template<int N>
43 VarOrRVar(const ImplicitVar<N> &u)
44 : var(u), is_rvar(false) {
45 }
46
47 const std::string &name() const {
48 if (is_rvar) {
49 return rvar.name();
50 } else {
51 return var.name();
52 }
53 }
54
55 Var var;
56 RVar rvar;
57 bool is_rvar;
58};
59
60 class ImageParam;
61
62 namespace Internal {
63 struct AssociativeOp;
64 class Function;
65 struct Split;
66 struct StorageDim;
67} // namespace Internal
68
69/** A single definition of a Func. May be a pure or update definition. */
70 class Stage {
71 /** Reference to the Function this stage (or definition) belongs to. */
72 Internal::Function function;
73 Internal::Definition definition;
74 /** Indicate which stage the definition belongs to (0 for initial
75 * definition, 1 for first update, etc.). */
76 size_t stage_index;
77 /** Pure Vars of the Function (from the init definition). */
78 std::vector<Var> dim_vars;
79
80 void set_dim_type(const VarOrRVar &var, Internal::ForType t);
81 void set_dim_device_api(const VarOrRVar &var, DeviceAPI device_api);
82 void split(const std::string &old, const std::string &outer, const std::string &inner,
83 const Expr &factor, bool exact, TailStrategy tail);
84 void remove(const std::string &var);
85
86 const std::vector<Internal::StorageDim> &storage_dims() const {
87 return function.schedule().storage_dims();
88 }
89
90 Stage &compute_with(LoopLevel loop_level, const std::map<std::string, LoopAlignStrategy> &align);
91
92 std::pair<std::vector<Internal::Split>, std::vector<Internal::Split>>
93 rfactor_validate_args(const std::vector<std::pair<RVar, Var>> &preserved, const Internal::AssociativeOp &prover_result);
94
95 public:
96 Stage(Internal::Function f, Internal::Definition d, size_t stage_index)
97 : function(std::move(f)), definition(std::move(d)), stage_index(stage_index) {
98 internal_assert(definition.defined());
99
100 dim_vars.reserve(function.args().size());
101 for (const auto &arg : function.args()) {
102 dim_vars.emplace_back(arg);
103 }
104 internal_assert(definition.args().size() == dim_vars.size());
105 }
106
107 /** Return the current StageSchedule associated with this Stage. For
108 * introspection only: to modify schedule, use the Func interface. */
109 const Internal::StageSchedule &get_schedule() const {
110 return definition.schedule();
111 }
112
113 /** Return a string describing the current var list taking into
114 * account all the splits, reorders, and tiles. */
115 std::string dump_argument_list() const;
116
117 /** Return the name of this stage, e.g. "f.update(2)" */
118 std::string name() const;
119
120 /** Calling rfactor() on an associative update definition a Func will split
121 * the update into an intermediate which computes the partial results and
122 * replaces the current update definition with a new definition which merges
123 * the partial results. If called on a init/pure definition, this will
124 * throw an error. rfactor() will automatically infer the associative reduction
125 * operator and identity of the operator. If it can't prove the operation
126 * is associative or if it cannot find an identity for that operator, this
127 * will throw an error. In addition, commutativity of the operator is required
128 * if rfactor() is called on the inner dimension but excluding the outer
129 * dimensions.
130 *
131 * rfactor() takes as input 'preserved', which is a list of <RVar, Var> pairs.
132 * The rvars not listed in 'preserved' are removed from the original Func and
133 * are lifted to the intermediate Func. The remaining rvars (the ones in
134 * 'preserved') are made pure in the intermediate Func. The intermediate Func's
135 * update definition inherits all scheduling directives (e.g. split,fuse, etc.)
136 * applied to the original Func's update definition. The loop order of the
137 * intermediate Func's update definition is the same as the original, although
138 * the RVars in 'preserved' are replaced by the new pure Vars. The loop order of the
139 * intermediate Func's init definition from innermost to outermost is the args'
140 * order of the original Func's init definition followed by the new pure Vars.
141 *
142 * The intermediate Func also inherits storage order from the original Func
143 * with the new pure Vars added to the outermost.
144 *
145 * For example, f.update(0).rfactor({{r.y, u}}) would rewrite a pipeline like this:
146 \code
147 f(x, y) = 0;
148 f(x, y) += g(r.x, r.y);
149 \endcode
150 * into a pipeline like this:
151 \code
152 f_intm(x, y, u) = 0;
153 f_intm(x, y, u) += g(r.x, u);
154
155 f(x, y) = 0;
156 f(x, y) += f_intm(x, y, r.y);
157 \endcode
158 *
159 * This has a variety of uses. You can use it to split computation of an associative reduction:
160 \code
161 f(x, y) = 10;
162 RDom r(0, 96);
163 f(x, y) = max(f(x, y), g(x, y, r.x));
164 f.update(0).split(r.x, rxo, rxi, 8).reorder(y, x).parallel(x);
165 f.update(0).rfactor({{rxo, u}}).compute_root().parallel(u).update(0).parallel(u);
166 \endcode
167 *
168 *, which is equivalent to:
169 \code
170 parallel for u = 0 to 11:
171 for y:
172 for x:
173 f_intm(x, y, u) = -inf
174 parallel for x:
175 for y:
176 parallel for u = 0 to 11:
177 for rxi = 0 to 7:
178 f_intm(x, y, u) = max(f_intm(x, y, u), g(8*u + rxi))
179 for y:
180 for x:
181 f(x, y) = 10
182 parallel for x:
183 for y:
184 for rxo = 0 to 11:
185 f(x, y) = max(f(x, y), f_intm(x, y, rxo))
186 \endcode
187 *
188 */
189 // @{
190 Func rfactor(const std::vector<std::pair<RVar, Var>> &preserved);
191 Func rfactor(const RVar &r, const Var &v);
192 // @}
193
194 /** Schedule the iteration over this stage to be fused with another
195 * stage 's' from outermost loop to a given LoopLevel. 'this' stage will
196 * be computed AFTER 's' in the innermost fused dimension. There should not
197 * be any dependencies between those two fused stages. If either of the
198 * stages being fused is a stage of an extern Func, this will throw an error.
199 *
200 * Note that the two stages that are fused together should have the same
201 * exact schedule from the outermost to the innermost fused dimension, and
202 * the stage we are calling compute_with on should not have specializations,
203 * e.g. f2.compute_with(f1, x) is allowed only if f2 has no specializations.
204 *
205 * Also, if a producer is desired to be computed at the fused loop level,
206 * the function passed to the compute_at() needs to be the "parent". Consider
207 * the following code:
208 \code
209 input(x, y) = x + y;
210 f(x, y) = input(x, y);
211 f(x, y) += 5;
212 g(x, y) = x - y;
213 g(x, y) += 10;
214 f.compute_with(g, y);
215 f.update().compute_with(g.update(), y);
216 \endcode
217 *
218 * To compute 'input' at the fused loop level at dimension y, we specify
219 * input.compute_at(g, y) instead of input.compute_at(f, y) since 'g' is
220 * the "parent" for this fused loop (i.e. 'g' is computed first before 'f'
221 * is computed). On the other hand, to compute 'input' at the innermost
222 * dimension of 'f', we specify input.compute_at(f, x) instead of
223 * input.compute_at(g, x) since the x dimension of 'f' is not fused
224 * (only the y dimension is).
225 *
226 * Given the constraints, this has a variety of uses. Consider the
227 * following code:
228 \code
229 f(x, y) = x + y;
230 g(x, y) = x - y;
231 h(x, y) = f(x, y) + g(x, y);
232 f.compute_root();
233 g.compute_root();
234 f.split(x, xo, xi, 8);
235 g.split(x, xo, xi, 8);
236 g.compute_with(f, xo);
237 \endcode
238 *
239 * This is equivalent to:
240 \code
241 for y:
242 for xo:
243 for xi:
244 f(8*xo + xi) = (8*xo + xi) + y
245 for xi:
246 g(8*xo + xi) = (8*xo + xi) - y
247 for y:
248 for x:
249 h(x, y) = f(x, y) + g(x, y)
250 \endcode
251 *
252 * The size of the dimensions of the stages computed_with do not have
253 * to match. Consider the following code where 'g' is half the size of 'f':
254 \code
255 Image<int> f_im(size, size), g_im(size/2, size/2);
256 input(x, y) = x + y;
257 f(x, y) = input(x, y);
258 g(x, y) = input(2*x, 2*y);
259 g.compute_with(f, y);
260 input.compute_at(f, y);
261 Pipeline({f, g}).realize({f_im, g_im});
262 \endcode
263 *
264 * This is equivalent to:
265 \code
266 for y = 0 to size-1:
267 for x = 0 to size-1:
268 input(x, y) = x + y;
269 for x = 0 to size-1:
270 f(x, y) = input(x, y)
271 for x = 0 to size/2-1:
272 if (y < size/2-1):
273 g(x, y) = input(2*x, 2*y)
274 \endcode
275 *
276 * 'align' specifies how the loop iteration of each dimension of the
277 * two stages being fused should be aligned in the fused loop nests
278 * (see LoopAlignStrategy for options). Consider the following loop nests:
279 \code
280 for z = f_min_z to f_max_z:
281 for y = f_min_y to f_max_y:
282 for x = f_min_x to f_max_x:
283 f(x, y, z) = x + y + z
284 for z = g_min_z to g_max_z:
285 for y = g_min_y to g_max_y:
286 for x = g_min_x to g_max_x:
287 g(x, y, z) = x - y - z
288 \endcode
289 *
290 * If no alignment strategy is specified, the following loop nest will be
291 * generated:
292 \code
293 for z = min(f_min_z, g_min_z) to max(f_max_z, g_max_z):
294 for y = min(f_min_y, g_min_y) to max(f_max_y, g_max_y):
295 for x = f_min_x to f_max_x:
296 if (f_min_z <= z <= f_max_z):
297 if (f_min_y <= y <= f_max_y):
298 f(x, y, z) = x + y + z
299 for x = g_min_x to g_max_x:
300 if (g_min_z <= z <= g_max_z):
301 if (g_min_y <= y <= g_max_y):
302 g(x, y, z) = x - y - z
303 \endcode
304 *
305 * Instead, these alignment strategies:
306 \code
307 g.compute_with(f, y, {{z, LoopAlignStrategy::AlignStart}, {y, LoopAlignStrategy::AlignEnd}});
308 \endcode
309 * will produce the following loop nest:
310 \code
311 f_loop_min_z = f_min_z
312 f_loop_max_z = max(f_max_z, (f_min_z - g_min_z) + g_max_z)
313 for z = f_min_z to f_loop_max_z:
314 f_loop_min_y = min(f_min_y, (f_max_y - g_max_y) + g_min_y)
315 f_loop_max_y = f_max_y
316 for y = f_loop_min_y to f_loop_max_y:
317 for x = f_min_x to f_max_x:
318 if (f_loop_min_z <= z <= f_loop_max_z):
319 if (f_loop_min_y <= y <= f_loop_max_y):
320 f(x, y, z) = x + y + z
321 for x = g_min_x to g_max_x:
322 g_shift_z = g_min_z - f_loop_min_z
323 g_shift_y = g_max_y - f_loop_max_y
324 if (g_min_z <= (z + g_shift_z) <= g_max_z):
325 if (g_min_y <= (y + g_shift_y) <= g_max_y):
326 g(x, y + g_shift_y, z + g_shift_z) = x - (y + g_shift_y) - (z + g_shift_z)
327 \endcode
328 *
329 * LoopAlignStrategy::AlignStart on dimension z will shift the loop iteration
330 * of 'g' at dimension z so that its starting value matches that of 'f'.
331 * Likewise, LoopAlignStrategy::AlignEnd on dimension y will shift the loop
332 * iteration of 'g' at dimension y so that its end value matches that of 'f'.
333 */
334 // @{
335 Stage &compute_with(LoopLevel loop_level, const std::vector<std::pair<VarOrRVar, LoopAlignStrategy>> &align);
336 Stage &compute_with(LoopLevel loop_level, LoopAlignStrategy align = LoopAlignStrategy::Auto);
337 Stage &compute_with(const Stage &s, const VarOrRVar &var, const std::vector<std::pair<VarOrRVar, LoopAlignStrategy>> &align);
338 Stage &compute_with(const Stage &s, const VarOrRVar &var, LoopAlignStrategy align = LoopAlignStrategy::Auto);
339 // @}
340
341 /** Scheduling calls that control how the domain of this stage is
342 * traversed. See the documentation for Func for the meanings. */
343 // @{
344
345 Stage &split(const VarOrRVar &old, const VarOrRVar &outer, const VarOrRVar &inner, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
346 Stage &fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRVar &fused);
347 Stage &serial(const VarOrRVar &var);
348 Stage &parallel(const VarOrRVar &var);
349 Stage &vectorize(const VarOrRVar &var);
350 Stage &unroll(const VarOrRVar &var);
351 Stage &parallel(const VarOrRVar &var, const Expr &task_size, TailStrategy tail = TailStrategy::Auto);
352 Stage &vectorize(const VarOrRVar &var, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
353 Stage &unroll(const VarOrRVar &var, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
354 Stage &partition(const VarOrRVar &var, Partition partition_policy);
355 Stage &never_partition_all();
356 Stage &never_partition(const std::vector<VarOrRVar> &vars);
357 Stage &always_partition_all();
358 Stage &always_partition(const std::vector<VarOrRVar> &vars);
359
360 Stage &tile(const VarOrRVar &x, const VarOrRVar &y,
361 const VarOrRVar &xo, const VarOrRVar &yo,
362 const VarOrRVar &xi, const VarOrRVar &yi, const Expr &xfactor, const Expr &yfactor,
363 TailStrategy tail = TailStrategy::Auto);
364 Stage &tile(const VarOrRVar &x, const VarOrRVar &y,
365 const VarOrRVar &xi, const VarOrRVar &yi,
366 const Expr &xfactor, const Expr &yfactor,
367 TailStrategy tail = TailStrategy::Auto);
368 Stage &tile(const std::vector<VarOrRVar> &previous,
369 const std::vector<VarOrRVar> &outers,
370 const std::vector<VarOrRVar> &inners,
371 const std::vector<Expr> &factors,
372 const std::vector<TailStrategy> &tails);
373 Stage &tile(const std::vector<VarOrRVar> &previous,
374 const std::vector<VarOrRVar> &outers,
375 const std::vector<VarOrRVar> &inners,
376 const std::vector<Expr> &factors,
377 TailStrategy tail = TailStrategy::Auto);
378 Stage &tile(const std::vector<VarOrRVar> &previous,
379 const std::vector<VarOrRVar> &inners,
380 const std::vector<Expr> &factors,
381 TailStrategy tail = TailStrategy::Auto);
382 Stage &reorder(const std::vector<VarOrRVar> &vars);
383
384 template<typename... Args>
385 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Stage &>
386 reorder(const VarOrRVar &x, const VarOrRVar &y, Args &&...args) {
387 std::vector<VarOrRVar> collected_args{x, y, std::forward<Args>(args)...};
388 return reorder(collected_args);
389 }
390
391 template<typename... Args>
392 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Stage &>
393 never_partition(const VarOrRVar &x, Args &&...args) {
394 std::vector<VarOrRVar> collected_args{x, std::forward<Args>(args)...};
395 return never_partition(collected_args);
396 }
397
398 template<typename... Args>
399 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Stage &>
400 always_partition(const VarOrRVar &x, Args &&...args) {
401 std::vector<VarOrRVar> collected_args{x, std::forward<Args>(args)...};
402 return always_partition(collected_args);
403 }
404
405 Stage &rename(const VarOrRVar &old_name, const VarOrRVar &new_name);
406 Stage specialize(const Expr &condition);
407 void specialize_fail(const std::string &message);
408
409 Stage &gpu_threads(const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
410 Stage &gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api = DeviceAPI::Default_GPU);
411 Stage &gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api = DeviceAPI::Default_GPU);
412
413 Stage &gpu_lanes(const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
414
415 Stage &gpu_single_thread(DeviceAPI device_api = DeviceAPI::Default_GPU);
416
417 Stage &gpu_blocks(const VarOrRVar &block_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
418 Stage &gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, DeviceAPI device_api = DeviceAPI::Default_GPU);
419 Stage &gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, DeviceAPI device_api = DeviceAPI::Default_GPU);
420
421 Stage &gpu(const VarOrRVar &block_x, const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
422 Stage &gpu(const VarOrRVar &block_x, const VarOrRVar &block_y,
423 const VarOrRVar &thread_x, const VarOrRVar &thread_y,
424 DeviceAPI device_api = DeviceAPI::Default_GPU);
425 Stage &gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z,
426 const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z,
427 DeviceAPI device_api = DeviceAPI::Default_GPU);
428
429 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &bx, const VarOrRVar &tx, const Expr &x_size,
430 TailStrategy tail = TailStrategy::Auto,
431 DeviceAPI device_api = DeviceAPI::Default_GPU);
432
433 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &tx, const Expr &x_size,
434 TailStrategy tail = TailStrategy::Auto,
435 DeviceAPI device_api = DeviceAPI::Default_GPU);
436 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &y,
437 const VarOrRVar &bx, const VarOrRVar &by,
438 const VarOrRVar &tx, const VarOrRVar &ty,
439 const Expr &x_size, const Expr &y_size,
440 TailStrategy tail = TailStrategy::Auto,
441 DeviceAPI device_api = DeviceAPI::Default_GPU);
442
443 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &y,
444 const VarOrRVar &tx, const VarOrRVar &ty,
445 const Expr &x_size, const Expr &y_size,
446 TailStrategy tail = TailStrategy::Auto,
447 DeviceAPI device_api = DeviceAPI::Default_GPU);
448
449 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z,
450 const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &bz,
451 const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz,
452 const Expr &x_size, const Expr &y_size, const Expr &z_size,
453 TailStrategy tail = TailStrategy::Auto,
454 DeviceAPI device_api = DeviceAPI::Default_GPU);
455 Stage &gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z,
456 const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz,
457 const Expr &x_size, const Expr &y_size, const Expr &z_size,
458 TailStrategy tail = TailStrategy::Auto,
459 DeviceAPI device_api = DeviceAPI::Default_GPU);
460
461 Stage &allow_race_conditions();
462 Stage &atomic(bool override_associativity_test = false);
463
464 Stage &hexagon(const VarOrRVar &x = Var::outermost());
465
466 Stage &prefetch(const Func &f, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
467 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf);
468 Stage &prefetch(const Parameter &param, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
469 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf);
470 template<typename T>
471 Stage &prefetch(const T &image, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
472 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf) {
473 return prefetch(image.parameter(), at, from, std::move(offset), strategy);
474 }
475 // @}
476
477 /** Get the Vars and RVars of this definition, from innermost out, with
478 * splits applied. This represents all the potentially-valid compute_at
479 * sites for this Stage. The RVars returned will be symbolic and not tied to
480 * a particular reduction domain, like the naked RVar objects used as split
481 * outputs. Note that this list by default will end with the sentinel
482 * Var::outermost. */
483 std::vector<VarOrRVar> split_vars() const;
484
485 /** Assert that this stage has intentionally been given no schedule, and
486 * suppress the warning about unscheduled update definitions that would
487 * otherwise fire. This counts as a schedule, so calling this twice on the
488 * same Stage will fail the assertion. */
489 void unscheduled();
490};
491
492// For backwards compatibility, keep the ScheduleHandle name.
493 typedef Stage ScheduleHandle;
494
495 class FuncTupleElementRef;
496
497/** A fragment of front-end syntax of the form f(x, y, z), where x, y,
498 * z are Vars or Exprs. If could be the left hand side of a definition or
499 * an update definition, or it could be a call to a function. We don't know
500 * until we see how this object gets used.
501 */
502 class FuncRef {
503 Internal::Function func;
504 int implicit_placeholder_pos;
505 int implicit_count;
506 std::vector<Expr> args;
507 std::vector<Expr> args_with_implicit_vars(const std::vector<Expr> &e) const;
508
509 /** Helper for function update by Tuple. If the function does not
510 * already have a pure definition, init_val will be used as RHS of
511 * each tuple element in the initial function definition. */
512 template<typename BinaryOp>
513 Stage func_ref_update(const Tuple &e, int init_val);
514
515 /** Helper for function update by Expr. If the function does not
516 * already have a pure definition, init_val will be used as RHS in
517 * the initial function definition. */
518 template<typename BinaryOp>
519 Stage func_ref_update(const Expr &e, int init_val);
520
521 public:
522 FuncRef(const Internal::Function &, const std::vector<Expr> &,
523 int placeholder_pos = -1, int count = 0);
524 FuncRef(Internal::Function, const std::vector<Var> &,
525 int placeholder_pos = -1, int count = 0);
526
527 /** Use this as the left-hand-side of a definition or an update definition
528 * (see \ref RDom).
529 */
530 Stage operator=(const Expr &);
531
532 /** Use this as the left-hand-side of a definition or an update definition
533 * for a Func with multiple outputs. */
534 Stage operator=(const Tuple &);
535
536 /** Define a stage that adds the given expression to this Func. If the
537 * expression refers to some RDom, this performs a sum reduction of the
538 * expression over the domain. If the function does not already have a
539 * pure definition, this sets it to zero.
540 */
541 // @{
542 Stage operator+=(const Expr &);
543 Stage operator+=(const Tuple &);
544 Stage operator+=(const FuncRef &);
545 // @}
546
547 /** Define a stage that adds the negative of the given expression to this
548 * Func. If the expression refers to some RDom, this performs a sum reduction
549 * of the negative of the expression over the domain. If the function does
550 * not already have a pure definition, this sets it to zero.
551 */
552 // @{
553 Stage operator-=(const Expr &);
554 Stage operator-=(const Tuple &);
555 Stage operator-=(const FuncRef &);
556 // @}
557
558 /** Define a stage that multiplies this Func by the given expression. If the
559 * expression refers to some RDom, this performs a product reduction of the
560 * expression over the domain. If the function does not already have a pure
561 * definition, this sets it to 1.
562 */
563 // @{
564 Stage operator*=(const Expr &);
565 Stage operator*=(const Tuple &);
566 Stage operator*=(const FuncRef &);
567 // @}
568
569 /** Define a stage that divides this Func by the given expression.
570 * If the expression refers to some RDom, this performs a product
571 * reduction of the inverse of the expression over the domain. If the
572 * function does not already have a pure definition, this sets it to 1.
573 */
574 // @{
575 Stage operator/=(const Expr &);
576 Stage operator/=(const Tuple &);
577 Stage operator/=(const FuncRef &);
578 // @}
579
580 /* Override the usual assignment operator, so that
581 * f(x, y) = g(x, y) defines f.
582 */
583 Stage operator=(const FuncRef &);
584
585 /** Use this as a call to the function, and not the left-hand-side
586 * of a definition. Only works for single-output Funcs. */
587 operator Expr() const;
588
589 /** Get the type(s) of the outputs of the Func to which this FuncRef refers.
590 *
591 * It is not legal to call type() unless the Func has non-Tuple elements.
592 *
593 * If the Func isn't yet defined, and was not specified with required types,
594 * a runtime error will occur.
595 *
596 * If the Func isn't yet defined, but *was* specified with required types,
597 * the requirements will be returned. */
598 // @{
599 const Type &type() const;
600 const std::vector<Type> &types() const;
601 // @}
602
603 /** When a FuncRef refers to a function that provides multiple
604 * outputs, you can access each output as an Expr using
605 * operator[].
606 */
607 FuncTupleElementRef operator[](int) const;
608
609 /** How many outputs does the function this refers to produce. */
610 size_t size() const;
611
612 /** Is this FuncRef syntactically equivalent to another one? */
613 bool equivalent_to(const FuncRef &other) const;
614
615 /** What function is this calling? */
616 Internal::Function function() const {
617 return func;
618 }
619};
620
621/** Explicit overloads of min and max for FuncRef. These exist to
622 * disambiguate calls to min on FuncRefs when a user has pulled both
623 * Halide::min and std::min into their namespace. */
624// @{
625 inline Expr min(const FuncRef &a, const FuncRef &b) {
626 return min(Expr(a), Expr(b));
627}
628 inline Expr max(const FuncRef &a, const FuncRef &b) {
629 return max(Expr(a), Expr(b));
630}
631// @}
632
633/** A fragment of front-end syntax of the form f(x, y, z)[index], where x, y,
634 * z are Vars or Exprs. If could be the left hand side of an update
635 * definition, or it could be a call to a function. We don't know
636 * until we see how this object gets used.
637 */
638 class FuncTupleElementRef {
639 FuncRef func_ref;
640 std::vector<Expr> args; // args to the function
641 int idx; // Index to function outputs
642
643 /** Helper function that generates a Tuple where element at 'idx' is set
644 * to 'e' and the rests are undef. */
645 Tuple values_with_undefs(const Expr &e) const;
646
647 public:
648 FuncTupleElementRef(const FuncRef &ref, const std::vector<Expr> &args, int idx);
649
650 /** Use this as the left-hand-side of an update definition of Tuple
651 * component 'idx' of a Func (see \ref RDom). The function must
652 * already have an initial definition.
653 */
654 Stage operator=(const Expr &e);
655
656 /** Define a stage that adds the given expression to Tuple component 'idx'
657 * of this Func. The other Tuple components are unchanged. If the expression
658 * refers to some RDom, this performs a sum reduction of the expression over
659 * the domain. The function must already have an initial definition.
660 */
661 Stage operator+=(const Expr &e);
662
663 /** Define a stage that adds the negative of the given expression to Tuple
664 * component 'idx' of this Func. The other Tuple components are unchanged.
665 * If the expression refers to some RDom, this performs a sum reduction of
666 * the negative of the expression over the domain. The function must already
667 * have an initial definition.
668 */
669 Stage operator-=(const Expr &e);
670
671 /** Define a stage that multiplies Tuple component 'idx' of this Func by
672 * the given expression. The other Tuple components are unchanged. If the
673 * expression refers to some RDom, this performs a product reduction of
674 * the expression over the domain. The function must already have an
675 * initial definition.
676 */
677 Stage operator*=(const Expr &e);
678
679 /** Define a stage that divides Tuple component 'idx' of this Func by
680 * the given expression. The other Tuple components are unchanged.
681 * If the expression refers to some RDom, this performs a product
682 * reduction of the inverse of the expression over the domain. The function
683 * must already have an initial definition.
684 */
685 Stage operator/=(const Expr &e);
686
687 /* Override the usual assignment operator, so that
688 * f(x, y)[index] = g(x, y) defines f.
689 */
690 Stage operator=(const FuncRef &e);
691
692 /** Use this as a call to Tuple component 'idx' of a Func, and not the
693 * left-hand-side of a definition. */
694 operator Expr() const;
695
696 /** What function is this calling? */
697 Internal::Function function() const {
698 return func_ref.function();
699 }
700
701 /** Return index to the function outputs. */
702 int index() const {
703 return idx;
704 }
705};
706
707 namespace Internal {
708 class IRMutator;
709} // namespace Internal
710
711/** Helper class for identifying purpose of an Expr passed to memoize.
712 */
713 class EvictionKey {
714 protected:
715 Expr key;
716 friend class Func;
717
718 public:
719 explicit EvictionKey(const Expr &expr = Expr())
720 : key(expr) {
721 }
722};
723
724/** A halide function. This class represents one stage in a Halide
725 * pipeline, and is the unit by which we schedule things. By default
726 * they are aggressively inlined, so you are encouraged to make lots
727 * of little functions, rather than storing things in Exprs. */
728 class Func {
729
730 /** A handle on the internal halide function that this
731 * represents */
732 Internal::Function func;
733
734 /** When you make a reference to this function with fewer
735 * arguments than it has dimensions, the argument list is bulked
736 * up with 'implicit' vars with canonical names. This lets you
737 * pass around partially applied Halide functions. */
738 // @{
739 std::pair<int, int> add_implicit_vars(std::vector<Var> &) const;
740 std::pair<int, int> add_implicit_vars(std::vector<Expr> &) const;
741 // @}
742
743 /** The imaging pipeline that outputs this Func alone. */
744 Pipeline pipeline_;
745
746 /** Get the imaging pipeline that outputs this Func alone,
747 * creating it (and freezing the Func) if necessary. */
748 Pipeline pipeline();
749
750 // Helper function for recursive reordering support
751 Func &reorder_storage(const std::vector<Var> &dims, size_t start);
752
753 void invalidate_cache();
754
755 public:
756 /** Declare a new undefined function with the given name */
757 explicit Func(const std::string &name);
758
759 /** Declare a new undefined function with the given name.
760 * The function will be constrained to represent Exprs of required_type.
761 * If required_dims is not AnyDims, the function will be constrained to exactly
762 * that many dimensions. */
763 explicit Func(const Type &required_type, int required_dims, const std::string &name);
764
765 /** Declare a new undefined function with the given name.
766 * If required_types is not empty, the function will be constrained to represent
767 * Tuples of the same arity and types. (If required_types is empty, there is no constraint.)
768 * If required_dims is not AnyDims, the function will be constrained to exactly
769 * that many dimensions. */
770 explicit Func(const std::vector<Type> &required_types, int required_dims, const std::string &name);
771
772 /** Declare a new undefined function with an
773 * automatically-generated unique name */
774 Func();
775
776 /** Declare a new function with an automatically-generated unique
777 * name, and define it to return the given expression (which may
778 * not contain free variables). */
779 explicit Func(const Expr &e);
780
781 /** Construct a new Func to wrap an existing, already-defined
782 * Function object. */
783 explicit Func(Internal::Function f);
784
785 /** Construct a new Func to wrap a Buffer. */
786 template<typename T, int Dims>
787 HALIDE_NO_USER_CODE_INLINE explicit Func(Buffer<T, Dims> &im)
788 : Func() {
789 (*this)(_) = im(_);
790 }
791
792 /** Evaluate this function over some rectangular domain and return
793 * the resulting buffer or buffers. Performs compilation if the
794 * Func has not previously been realized and compile_jit has not
795 * been called. If the final stage of the pipeline is on the GPU,
796 * data is copied back to the host before being returned. The
797 * returned Realization should probably be instantly converted to
798 * a Buffer class of the appropriate type. That is, do this:
799 *
800 \code
801 f(x) = sin(x);
802 Buffer<float> im = f.realize(...);
803 \endcode
804 *
805 * If your Func has multiple values, because you defined it using
806 * a Tuple, then casting the result of a realize call to a buffer
807 * or image will produce a run-time error. Instead you should do the
808 * following:
809 *
810 \code
811 f(x) = Tuple(x, sin(x));
812 Realization r = f.realize(...);
813 Buffer<int> im0 = r[0];
814 Buffer<float> im1 = r[1];
815 \endcode
816 *
817 * In Halide formal arguments of a computation are specified using
818 * Param<T> and ImageParam objects in the expressions defining the
819 * computation. Note that this method is not thread-safe, in that
820 * Param<T> and ImageParam are globals shared by all threads; to call
821 * jitted code in a thread-safe manner, use compile_to_callable() instead.
822 *
823 \code
824 Param<int32> p(42);
825 ImageParam img(Int(32), 1);
826 f(x) = img(x) + p;
827
828 Buffer<int32_t) arg_img(10, 10);
829 <fill in arg_img...>
830
831 Target t = get_jit_target_from_environment();
832 Buffer<int32_t> result = f.realize({10, 10}, t);
833 \endcode
834 *
835 * Alternatively, an initializer list can be used
836 * directly in the realize call to pass this information:
837 *
838 \code
839 Param<int32> p(42);
840 ImageParam img(Int(32), 1);
841 f(x) = img(x) + p;
842
843 Buffer<int32_t) arg_img(10, 10);
844 <fill in arg_img...>
845
846 Target t = get_jit_target_from_environment();
847 Buffer<int32_t> result = f.realize({10, 10}, t, { { p, 17 }, { img, arg_img } });
848 \endcode
849 *
850 * If the Func cannot be realized into a buffer of the given size
851 * due to scheduling constraints on scattering update definitions,
852 * it will be realized into a larger buffer of the minimum size
853 * possible, and a cropped view at the requested size will be
854 * returned. It is thus not safe to assume the returned buffers
855 * are contiguous in memory. This behavior can be disabled with
856 * the NoBoundsQuery target flag, in which case an error about
857 * writing out of bounds on the output buffer will trigger
858 * instead.
859 *
860 */
861 Realization realize(std::vector<int32_t> sizes = {}, const Target &target = Target());
862
863 /** Same as above, but takes a custom user-provided context to be
864 * passed to runtime functions. This can be used to pass state to
865 * runtime overrides in a thread-safe manner. A nullptr context is
866 * legal, and is equivalent to calling the variant of realize
867 * that does not take a context. */
868 Realization realize(JITUserContext *context,
869 std::vector<int32_t> sizes = {},
870 const Target &target = Target());
871
872 /** Evaluate this function into an existing allocated buffer or
873 * buffers. If the buffer is also one of the arguments to the
874 * function, strange things may happen, as the pipeline isn't
875 * necessarily safe to run in-place. If you pass multiple buffers,
876 * they must have matching sizes. This form of realize does *not*
877 * automatically copy data back from the GPU. */
878 void realize(Pipeline::RealizationArg outputs, const Target &target = Target());
879
880 /** Same as above, but takes a custom user-provided context to be
881 * passed to runtime functions. This can be used to pass state to
882 * runtime overrides in a thread-safe manner. A nullptr context is
883 * legal, and is equivalent to calling the variant of realize
884 * that does not take a context. */
885 void realize(JITUserContext *context,
886 Pipeline::RealizationArg outputs,
887 const Target &target = Target());
888
889 /** For a given size of output, or a given output buffer,
890 * determine the bounds required of all unbound ImageParams
891 * referenced. Communicates the result by allocating new buffers
892 * of the appropriate size and binding them to the unbound
893 * ImageParams.
894 */
895 // @{
896 void infer_input_bounds(const std::vector<int32_t> &sizes,
897 const Target &target = get_jit_target_from_environment());
898 void infer_input_bounds(Pipeline::RealizationArg outputs,
899 const Target &target = get_jit_target_from_environment());
900 // @}
901
902 /** Versions of infer_input_bounds that take a custom user context
903 * to pass to runtime functions. */
904 // @{
905 void infer_input_bounds(JITUserContext *context,
906 const std::vector<int32_t> &sizes,
907 const Target &target = get_jit_target_from_environment());
908 void infer_input_bounds(JITUserContext *context,
909 Pipeline::RealizationArg outputs,
910 const Target &target = get_jit_target_from_environment());
911 // @}
912 /** Statically compile this function to llvm bitcode, with the
913 * given filename (which should probably end in .bc), type
914 * signature, and C function name (which defaults to the same name
915 * as this halide function */
916 //@{
917 void compile_to_bitcode(const std::string &filename, const std::vector<Argument> &, const std::string &fn_name,
918 const Target &target = get_target_from_environment());
919 void compile_to_bitcode(const std::string &filename, const std::vector<Argument> &,
920 const Target &target = get_target_from_environment());
921 // @}
922
923 /** Statically compile this function to llvm assembly, with the
924 * given filename (which should probably end in .ll), type
925 * signature, and C function name (which defaults to the same name
926 * as this halide function */
927 //@{
928 void compile_to_llvm_assembly(const std::string &filename, const std::vector<Argument> &, const std::string &fn_name,
929 const Target &target = get_target_from_environment());
930 void compile_to_llvm_assembly(const std::string &filename, const std::vector<Argument> &,
931 const Target &target = get_target_from_environment());
932 // @}
933
934 /** Statically compile this function to an object file, with the
935 * given filename (which should probably end in .o or .obj), type
936 * signature, and C function name (which defaults to the same name
937 * as this halide function. You probably don't want to use this
938 * directly; call compile_to_static_library or compile_to_file instead. */
939 //@{
940 void compile_to_object(const std::string &filename, const std::vector<Argument> &, const std::string &fn_name,
941 const Target &target = get_target_from_environment());
942 void compile_to_object(const std::string &filename, const std::vector<Argument> &,
943 const Target &target = get_target_from_environment());
944 // @}
945
946 /** Emit a header file with the given filename for this
947 * function. The header will define a function with the type
948 * signature given by the second argument, and a name given by the
949 * third. The name defaults to the same name as this halide
950 * function. You don't actually have to have defined this function
951 * yet to call this. You probably don't want to use this directly;
952 * call compile_to_static_library or compile_to_file instead. */
953 void compile_to_header(const std::string &filename, const std::vector<Argument> &, const std::string &fn_name = "",
954 const Target &target = get_target_from_environment());
955
956 /** Statically compile this function to text assembly equivalent
957 * to the object file generated by compile_to_object. This is
958 * useful for checking what Halide is producing without having to
959 * disassemble anything, or if you need to feed the assembly into
960 * some custom toolchain to produce an object file (e.g. iOS) */
961 //@{
962 void compile_to_assembly(const std::string &filename, const std::vector<Argument> &, const std::string &fn_name,
963 const Target &target = get_target_from_environment());
964 void compile_to_assembly(const std::string &filename, const std::vector<Argument> &,
965 const Target &target = get_target_from_environment());
966 // @}
967
968 /** Statically compile this function to C source code. This is
969 * useful for providing fallback code paths that will compile on
970 * many platforms. Vectorization will fail, and parallelization
971 * will produce serial code. */
972 void compile_to_c(const std::string &filename,
973 const std::vector<Argument> &,
974 const std::string &fn_name = "",
975 const Target &target = get_target_from_environment());
976
977 /** Write out an internal representation of lowered code. Useful
978 * for analyzing and debugging scheduling. Can emit html or plain
979 * text. */
980 void compile_to_lowered_stmt(const std::string &filename,
981 const std::vector<Argument> &args,
982 StmtOutputFormat fmt = Text,
983 const Target &target = get_target_from_environment());
984
985 /** Write out a conceptual representation of lowered code, before any parallel loop
986 * get factored out into separate functions, or GPU loops are offloaded to kernel code.r
987 * Useful for analyzing and debugging scheduling. Can emit html or plain text. */
988 void compile_to_conceptual_stmt(const std::string &filename,
989 const std::vector<Argument> &args,
990 StmtOutputFormat fmt = Text,
991 const Target &target = get_target_from_environment());
992
993 /** Write out the loop nests specified by the schedule for this
994 * Function. Helpful for understanding what a schedule is
995 * doing. */
996 void print_loop_nest();
997
998 /** Compile to object file and header pair, with the given
999 * arguments. The name defaults to the same name as this halide
1000 * function.
1001 */
1002 void compile_to_file(const std::string &filename_prefix, const std::vector<Argument> &args,
1003 const std::string &fn_name = "",
1004 const Target &target = get_target_from_environment());
1005
1006 /** Compile to static-library file and header pair, with the given
1007 * arguments. The name defaults to the same name as this halide
1008 * function.
1009 */
1010 void compile_to_static_library(const std::string &filename_prefix, const std::vector<Argument> &args,
1011 const std::string &fn_name = "",
1012 const Target &target = get_target_from_environment());
1013
1014 /** Compile to static-library file and header pair once for each target;
1015 * each resulting function will be considered (in order) via halide_can_use_target_features()
1016 * at runtime, with the first appropriate match being selected for subsequent use.
1017 * This is typically useful for specializations that may vary unpredictably by machine
1018 * (e.g., SSE4.1/AVX/AVX2 on x86 desktop machines).
1019 * All targets must have identical arch-os-bits.
1020 */
1021 void compile_to_multitarget_static_library(const std::string &filename_prefix,
1022 const std::vector<Argument> &args,
1023 const std::vector<Target> &targets);
1024
1025 /** Like compile_to_multitarget_static_library(), except that the object files
1026 * are all output as object files (rather than bundled into a static library).
1027 *
1028 * `suffixes` is an optional list of strings to use for as the suffix for each object
1029 * file. If nonempty, it must be the same length as `targets`. (If empty, Target::to_string()
1030 * will be used for each suffix.)
1031 *
1032 * Note that if `targets.size()` > 1, the wrapper code (to select the subtarget)
1033 * will be generated with the filename `${filename_prefix}_wrapper.o`
1034 *
1035 * Note that if `targets.size()` > 1 and `no_runtime` is not specified, the runtime
1036 * will be generated with the filename `${filename_prefix}_runtime.o`
1037 */
1038 void compile_to_multitarget_object_files(const std::string &filename_prefix,
1039 const std::vector<Argument> &args,
1040 const std::vector<Target> &targets,
1041 const std::vector<std::string> &suffixes);
1042
1043 /** Store an internal representation of lowered code as a self
1044 * contained Module suitable for further compilation. */
1045 Module compile_to_module(const std::vector<Argument> &args, const std::string &fn_name = "",
1046 const Target &target = get_target_from_environment());
1047
1048 /** Compile and generate multiple target files with single call.
1049 * Deduces target files based on filenames specified in
1050 * output_files map.
1051 */
1052 void compile_to(const std::map<OutputFileType, std::string> &output_files,
1053 const std::vector<Argument> &args,
1054 const std::string &fn_name,
1055 const Target &target = get_target_from_environment());
1056
1057 /** Eagerly jit compile the function to machine code. This
1058 * normally happens on the first call to realize. If you're
1059 * running your halide pipeline inside time-sensitive code and
1060 * wish to avoid including the time taken to compile a pipeline,
1061 * then you can call this ahead of time. Default is to use the Target
1062 * returned from Halide::get_jit_target_from_environment()
1063 */
1064 void compile_jit(const Target &target = get_jit_target_from_environment());
1065
1066 /** Get a struct containing the currently set custom functions
1067 * used by JIT. This can be mutated. Changes will take effect the
1068 * next time this Func is realized. */
1069 JITHandlers &jit_handlers();
1070
1071 /** Eagerly jit compile the function to machine code and return a callable
1072 * struct that behaves like a function pointer. The calling convention
1073 * will exactly match that of an AOT-compiled version of this Func
1074 * with the same Argument list.
1075 */
1076 Callable compile_to_callable(const std::vector<Argument> &args,
1077 const Target &target = get_jit_target_from_environment());
1078
1079 /** Add a custom pass to be used during lowering. It is run after
1080 * all other lowering passes. Can be used to verify properties of
1081 * the lowered Stmt, instrument it with extra code, or otherwise
1082 * modify it. The Func takes ownership of the pass, and will call
1083 * delete on it when the Func goes out of scope. So don't pass a
1084 * stack object, or share pass instances between multiple
1085 * Funcs. */
1086 template<typename T>
1087 void add_custom_lowering_pass(T *pass) {
1088 // Template instantiate a custom deleter for this type, then
1089 // wrap in a lambda. The custom deleter lives in user code, so
1090 // that deletion is on the same heap as construction (I hate Windows).
1091 add_custom_lowering_pass(pass, [pass]() { delete_lowering_pass<T>(pass); });
1092 }
1093
1094 /** Add a custom pass to be used during lowering, with the
1095 * function that will be called to delete it also passed in. Set
1096 * it to nullptr if you wish to retain ownership of the object. */
1097 void add_custom_lowering_pass(Internal::IRMutator *pass, std::function<void()> deleter);
1098
1099 /** Remove all previously-set custom lowering passes */
1100 void clear_custom_lowering_passes();
1101
1102 /** Get the custom lowering passes. */
1103 const std::vector<CustomLoweringPass> &custom_lowering_passes();
1104
1105 /** When this function is compiled, include code that dumps its
1106 * values to a file after it is realized, for the purpose of
1107 * debugging.
1108 *
1109 * If filename ends in ".tif" or ".tiff" (case insensitive) the file
1110 * is in TIFF format and can be read by standard tools. Oherwise, the
1111 * file format is as follows:
1112 *
1113 * All data is in the byte-order of the target platform. First, a
1114 * 20 byte-header containing four 32-bit ints, giving the extents
1115 * of the first four dimensions. Dimensions beyond four are
1116 * folded into the fourth. Then, a fifth 32-bit int giving the
1117 * data type of the function. The typecodes are given by: float =
1118 * 0, double = 1, uint8_t = 2, int8_t = 3, uint16_t = 4, int16_t =
1119 * 5, uint32_t = 6, int32_t = 7, uint64_t = 8, int64_t = 9. The
1120 * data follows the header, as a densely packed array of the given
1121 * size and the given type. If given the extension .tmp, this file
1122 * format can be natively read by the program ImageStack. */
1123 void debug_to_file(const std::string &filename);
1124
1125 /** The name of this function, either given during construction,
1126 * or automatically generated. */
1127 const std::string &name() const;
1128
1129 /** Get the pure arguments. */
1130 std::vector<Var> args() const;
1131
1132 /** The right-hand-side value of the pure definition of this
1133 * function. Causes an error if there's no pure definition, or if
1134 * the function is defined to return multiple values. */
1135 Expr value() const;
1136
1137 /** The values returned by this function. An error if the function
1138 * has not been been defined. Returns a Tuple with one element for
1139 * functions defined to return a single value. */
1140 Tuple values() const;
1141
1142 /** Does this function have at least a pure definition. */
1143 bool defined() const;
1144
1145 /** Get the left-hand-side of the update definition. An empty
1146 * vector if there's no update definition. If there are
1147 * multiple update definitions for this function, use the
1148 * argument to select which one you want. */
1149 const std::vector<Expr> &update_args(int idx = 0) const;
1150
1151 /** Get the right-hand-side of an update definition. An error if
1152 * there's no update definition. If there are multiple
1153 * update definitions for this function, use the argument to
1154 * select which one you want. */
1155 Expr update_value(int idx = 0) const;
1156
1157 /** Get the right-hand-side of an update definition for
1158 * functions that returns multiple values. An error if there's no
1159 * update definition. Returns a Tuple with one element for
1160 * functions that return a single value. */
1161 Tuple update_values(int idx = 0) const;
1162
1163 /** Get the RVars of the reduction domain for an update definition, if there is
1164 * one. */
1165 std::vector<RVar> rvars(int idx = 0) const;
1166
1167 /** Does this function have at least one update definition? */
1168 bool has_update_definition() const;
1169
1170 /** How many update definitions does this function have? */
1171 int num_update_definitions() const;
1172
1173 /** Is this function an external stage? That is, was it defined
1174 * using define_extern? */
1175 bool is_extern() const;
1176
1177 /** Add an extern definition for this Func. This lets you define a
1178 * Func that represents an external pipeline stage. You can, for
1179 * example, use it to wrap a call to an extern library such as
1180 * fftw. */
1181 // @{
1182 void define_extern(const std::string &function_name,
1183 const std::vector<ExternFuncArgument> &params, Type t,
1184 int dimensionality,
1185 NameMangling mangling = NameMangling::Default,
1186 DeviceAPI device_api = DeviceAPI::Host) {
1187 define_extern(function_name, params, t,
1188 Internal::make_argument_list(dimensionality), mangling,
1189 device_api);
1190 }
1191
1192 void define_extern(const std::string &function_name,
1193 const std::vector<ExternFuncArgument> &params,
1194 const std::vector<Type> &types, int dimensionality,
1195 NameMangling mangling = NameMangling::Default,
1196 DeviceAPI device_api = DeviceAPI::Host) {
1197 define_extern(function_name, params, types,
1198 Internal::make_argument_list(dimensionality), mangling,
1199 device_api);
1200 }
1201
1202 void define_extern(const std::string &function_name,
1203 const std::vector<ExternFuncArgument> &params, Type t,
1204 const std::vector<Var> &arguments,
1205 NameMangling mangling = NameMangling::Default,
1206 DeviceAPI device_api = DeviceAPI::Host) {
1207 define_extern(function_name, params, std::vector<Type>{t}, arguments,
1208 mangling, device_api);
1209 }
1210
1211 void define_extern(const std::string &function_name,
1212 const std::vector<ExternFuncArgument> &params,
1213 const std::vector<Type> &types,
1214 const std::vector<Var> &arguments,
1215 NameMangling mangling = NameMangling::Default,
1216 DeviceAPI device_api = DeviceAPI::Host);
1217 // @}
1218
1219 /** Get the type(s) of the outputs of this Func.
1220 *
1221 * It is not legal to call type() unless the Func has non-Tuple elements.
1222 *
1223 * If the Func isn't yet defined, and was not specified with required types,
1224 * a runtime error will occur.
1225 *
1226 * If the Func isn't yet defined, but *was* specified with required types,
1227 * the requirements will be returned. */
1228 // @{
1229 const Type &type() const;
1230 const std::vector<Type> &types() const;
1231 // @}
1232
1233 /** Get the number of outputs of this Func. Corresponds to the
1234 * size of the Tuple this Func was defined to return.
1235 * If the Func isn't yet defined, but was specified with required types,
1236 * the number of outputs specified in the requirements will be returned. */
1237 int outputs() const;
1238
1239 /** Get the name of the extern function called for an extern
1240 * definition. */
1241 const std::string &extern_function_name() const;
1242
1243 /** The dimensionality (number of arguments) of this function.
1244 * If the Func isn't yet defined, but was specified with required dimensionality,
1245 * the dimensionality specified in the requirements will be returned. */
1246 int dimensions() const;
1247
1248 /** Construct either the left-hand-side of a definition, or a call
1249 * to a functions that happens to only contain vars as
1250 * arguments. If the function has already been defined, and fewer
1251 * arguments are given than the function has dimensions, then
1252 * enough implicit vars are added to the end of the argument list
1253 * to make up the difference (see \ref Var::implicit) */
1254 // @{
1255 FuncRef operator()(std::vector<Var>) const;
1256
1257 template<typename... Args>
1258 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<Var, Args...>::value, FuncRef>
1259 operator()(Args &&...args) const {
1260 std::vector<Var> collected_args{std::forward<Args>(args)...};
1261 return this->operator()(collected_args);
1262 }
1263 // @}
1264
1265 /** Either calls to the function, or the left-hand-side of
1266 * an update definition (see \ref RDom). If the function has
1267 * already been defined, and fewer arguments are given than the
1268 * function has dimensions, then enough implicit vars are added to
1269 * the end of the argument list to make up the difference. (see
1270 * \ref Var::implicit)*/
1271 // @{
1272 FuncRef operator()(std::vector<Expr>) const;
1273
1274 template<typename... Args>
1275 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<Expr, Args...>::value, FuncRef>
1276 operator()(const Expr &x, Args &&...args) const {
1277 std::vector<Expr> collected_args{x, std::forward<Args>(args)...};
1278 return (*this)(collected_args);
1279 }
1280 // @}
1281
1282 /** Creates and returns a new identity Func that wraps this Func. During
1283 * compilation, Halide replaces all calls to this Func done by 'f'
1284 * with calls to the wrapper. If this Func is already wrapped for
1285 * use in 'f', will return the existing wrapper.
1286 *
1287 * For example, g.in(f) would rewrite a pipeline like this:
1288 \code
1289 g(x, y) = ...
1290 f(x, y) = ... g(x, y) ...
1291 \endcode
1292 * into a pipeline like this:
1293 \code
1294 g(x, y) = ...
1295 g_wrap(x, y) = g(x, y)
1296 f(x, y) = ... g_wrap(x, y)
1297 \endcode
1298 *
1299 * This has a variety of uses. You can use it to schedule this
1300 * Func differently in the different places it is used:
1301 \code
1302 g(x, y) = ...
1303 f1(x, y) = ... g(x, y) ...
1304 f2(x, y) = ... g(x, y) ...
1305 g.in(f1).compute_at(f1, y).vectorize(x, 8);
1306 g.in(f2).compute_at(f2, x).unroll(x);
1307 \endcode
1308 *
1309 * You can also use it to stage loads from this Func via some
1310 * intermediate buffer (perhaps on the stack as in
1311 * test/performance/block_transpose.cpp, or in shared GPU memory
1312 * as in test/performance/wrap.cpp). In this we compute the
1313 * wrapper at tiles of the consuming Funcs like so:
1314 \code
1315 g.compute_root()...
1316 g.in(f).compute_at(f, tiles)...
1317 \endcode
1318 *
1319 * Func::in() can also be used to compute pieces of a Func into a
1320 * smaller scratch buffer (perhaps on the GPU) and then copy them
1321 * into a larger output buffer one tile at a time. See
1322 * apps/interpolate/interpolate.cpp for an example of this. In
1323 * this case we compute the Func at tiles of its own wrapper:
1324 \code
1325 f.in(g).compute_root().gpu_tile(...)...
1326 f.compute_at(f.in(g), tiles)...
1327 \endcode
1328 *
1329 * A similar use of Func::in() wrapping Funcs with multiple update
1330 * stages in a pure wrapper. The following code:
1331 \code
1332 f(x, y) = x + y;
1333 f(x, y) += 5;
1334 g(x, y) = f(x, y);
1335 f.compute_root();
1336 \endcode
1337 *
1338 * Is equivalent to:
1339 \code
1340 for y:
1341 for x:
1342 f(x, y) = x + y;
1343 for y:
1344 for x:
1345 f(x, y) += 5
1346 for y:
1347 for x:
1348 g(x, y) = f(x, y)
1349 \endcode
1350 * using Func::in(), we can write:
1351 \code
1352 f(x, y) = x + y;
1353 f(x, y) += 5;
1354 g(x, y) = f(x, y);
1355 f.in(g).compute_root();
1356 \endcode
1357 * which instead produces:
1358 \code
1359 for y:
1360 for x:
1361 f(x, y) = x + y;
1362 f(x, y) += 5
1363 f_wrap(x, y) = f(x, y)
1364 for y:
1365 for x:
1366 g(x, y) = f_wrap(x, y)
1367 \endcode
1368 */
1369 Func in(const Func &f);
1370
1371 /** Create and return an identity wrapper shared by all the Funcs in
1372 * 'fs'. If any of the Funcs in 'fs' already have a custom wrapper,
1373 * this will throw an error. */
1374 Func in(const std::vector<Func> &fs);
1375
1376 /** Create and return a global identity wrapper, which wraps all calls to
1377 * this Func by any other Func. If a global wrapper already exists,
1378 * returns it. The global identity wrapper is only used by callers for
1379 * which no custom wrapper has been specified.
1380 */
1381 Func in();
1382
1383 /** Similar to \ref Func::in; however, instead of replacing the call to
1384 * this Func with an identity Func that refers to it, this replaces the
1385 * call with a clone of this Func.
1386 *
1387 * For example, f.clone_in(g) would rewrite a pipeline like this:
1388 \code
1389 f(x, y) = x + y;
1390 g(x, y) = f(x, y) + 2;
1391 h(x, y) = f(x, y) - 3;
1392 \endcode
1393 * into a pipeline like this:
1394 \code
1395 f(x, y) = x + y;
1396 f_clone(x, y) = x + y;
1397 g(x, y) = f_clone(x, y) + 2;
1398 h(x, y) = f(x, y) - 3;
1399 \endcode
1400 *
1401 */
1402 //@{
1403 Func clone_in(const Func &f);
1404 Func clone_in(const std::vector<Func> &fs);
1405 //@}
1406
1407 /** Declare that this function should be implemented by a call to
1408 * halide_buffer_copy with the given target device API. Asserts
1409 * that the Func has a pure definition which is a simple call to a
1410 * single input, and no update definitions. The wrapper Funcs
1411 * returned by in() are suitable candidates. Consumes all pure
1412 * variables, and rewrites the Func to have an extern definition
1413 * that calls halide_buffer_copy. */
1414 Func copy_to_device(DeviceAPI d = DeviceAPI::Default_GPU);
1415
1416 /** Declare that this function should be implemented by a call to
1417 * halide_buffer_copy with a NULL target device API. Equivalent to
1418 * copy_to_device(DeviceAPI::Host). Asserts that the Func has a
1419 * pure definition which is a simple call to a single input, and
1420 * no update definitions. The wrapper Funcs returned by in() are
1421 * suitable candidates. Consumes all pure variables, and rewrites
1422 * the Func to have an extern definition that calls
1423 * halide_buffer_copy.
1424 *
1425 * Note that if the source Func is already valid in host memory,
1426 * this compiles to code that does the minimum number of calls to
1427 * memcpy.
1428 */
1429 Func copy_to_host();
1430
1431 /** Split a dimension into inner and outer subdimensions with the
1432 * given names, where the inner dimension iterates from 0 to
1433 * factor-1. The inner and outer subdimensions can then be dealt
1434 * with using the other scheduling calls. It's ok to reuse the old
1435 * variable name as either the inner or outer variable. The final
1436 * argument specifies how the tail should be handled if the split
1437 * factor does not provably divide the extent. */
1438 Func &split(const VarOrRVar &old, const VarOrRVar &outer, const VarOrRVar &inner, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
1439
1440 /** Join two dimensions into a single fused dimension. The fused dimension
1441 * covers the product of the extents of the inner and outer dimensions
1442 * given. The loop type (e.g. parallel, vectorized) of the resulting fused
1443 * dimension is inherited from the first argument. */
1444 Func &fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRVar &fused);
1445
1446 /** Mark a dimension to be traversed serially. This is the default. */
1447 Func &serial(const VarOrRVar &var);
1448
1449 /** Mark a dimension to be traversed in parallel */
1450 Func &parallel(const VarOrRVar &var);
1451
1452 /** Split a dimension by the given task_size, and the parallelize the
1453 * outer dimension. This creates parallel tasks that have size
1454 * task_size. After this call, var refers to the outer dimension of
1455 * the split. The inner dimension has a new anonymous name. If you
1456 * wish to mutate it, or schedule with respect to it, do the split
1457 * manually. */
1458 Func &parallel(const VarOrRVar &var, const Expr &task_size, TailStrategy tail = TailStrategy::Auto);
1459
1460 /** Mark a dimension to be computed all-at-once as a single
1461 * vector. The dimension should have constant extent -
1462 * e.g. because it is the inner dimension following a split by a
1463 * constant factor. For most uses of vectorize you want the two
1464 * argument form. The variable to be vectorized should be the
1465 * innermost one. */
1466 Func &vectorize(const VarOrRVar &var);
1467
1468 /** Mark a dimension to be completely unrolled. The dimension
1469 * should have constant extent - e.g. because it is the inner
1470 * dimension following a split by a constant factor. For most uses
1471 * of unroll you want the two-argument form. */
1472 Func &unroll(const VarOrRVar &var);
1473
1474 /** Split a dimension by the given factor, then vectorize the
1475 * inner dimension. This is how you vectorize a loop of unknown
1476 * size. The variable to be vectorized should be the innermost
1477 * one. After this call, var refers to the outer dimension of the
1478 * split. 'factor' must be an integer. */
1479 Func &vectorize(const VarOrRVar &var, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
1480
1481 /** Split a dimension by the given factor, then unroll the inner
1482 * dimension. This is how you unroll a loop of unknown size by
1483 * some constant factor. After this call, var refers to the outer
1484 * dimension of the split. 'factor' must be an integer. */
1485 Func &unroll(const VarOrRVar &var, const Expr &factor, TailStrategy tail = TailStrategy::Auto);
1486
1487 /** Set the loop partition policy. Loop partitioning can be useful to
1488 * optimize boundary conditions (such as clamp_edge). Loop partitioning
1489 * splits a for loop into three for loops: a prologue, a steady-state,
1490 * and an epilogue.
1491 * The default policy is Auto. */
1492 Func &partition(const VarOrRVar &var, Partition partition_policy);
1493
1494 /** Set the loop partition policy to Never for a vector of Vars and
1495 * RVars. */
1496 Func &never_partition(const std::vector<VarOrRVar> &vars);
1497
1498 /** Set the loop partition policy to Never for some number of Vars and RVars. */
1499 template<typename... Args>
1500 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Func &>
1501 never_partition(const VarOrRVar &x, Args &&...args) {
1502 std::vector<VarOrRVar> collected_args{x, std::forward<Args>(args)...};
1503 return never_partition(collected_args);
1504 }
1505
1506 /** Set the loop partition policy to Never for all Vars and RVar of the
1507 * initial definition of the Func. It must be called separately on any
1508 * update definitions. */
1509 Func &never_partition_all();
1510
1511 /** Set the loop partition policy to Always for a vector of Vars and
1512 * RVars. */
1513 Func &always_partition(const std::vector<VarOrRVar> &vars);
1514
1515 /** Set the loop partition policy to Always for some number of Vars and RVars. */
1516 template<typename... Args>
1517 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Func &>
1518 always_partition(const VarOrRVar &x, Args &&...args) {
1519 std::vector<VarOrRVar> collected_args{x, std::forward<Args>(args)...};
1520 return always_partition(collected_args);
1521 }
1522
1523 /** Set the loop partition policy to Always for all Vars and RVar of the
1524 * initial definition of the Func. It must be called separately on any
1525 * update definitions. */
1526 Func &always_partition_all();
1527
1528 /** Statically declare that the range over which a function should
1529 * be evaluated is given by the second and third arguments. This
1530 * can let Halide perform some optimizations. E.g. if you know
1531 * there are going to be 4 color channels, you can completely
1532 * vectorize the color channel dimension without the overhead of
1533 * splitting it up. If bounds inference decides that it requires
1534 * more of this function than the bounds you have stated, a
1535 * runtime error will occur when you try to run your pipeline. */
1536 Func &bound(const Var &var, Expr min, Expr extent);
1537
1538 /** Statically declare the range over which the function will be
1539 * evaluated in the general case. This provides a basis for the auto
1540 * scheduler to make trade-offs and scheduling decisions. The auto
1541 * generated schedules might break when the sizes of the dimensions are
1542 * very different from the estimates specified. These estimates are used
1543 * only by the auto scheduler if the function is a pipeline output. */
1544 Func &set_estimate(const Var &var, const Expr &min, const Expr &extent);
1545
1546 /** Set (min, extent) estimates for all dimensions in the Func
1547 * at once; this is equivalent to calling `set_estimate(args()[n], min, extent)`
1548 * repeatedly, but slightly terser. The size of the estimates vector
1549 * must match the dimensionality of the Func. */
1550 Func &set_estimates(const Region &estimates);
1551
1552 /** Expand the region computed so that the min coordinates is
1553 * congruent to 'remainder' modulo 'modulus', and the extent is a
1554 * multiple of 'modulus'. For example, f.align_bounds(x, 2) forces
1555 * the min and extent realized to be even, and calling
1556 * f.align_bounds(x, 2, 1) forces the min to be odd and the extent
1557 * to be even. The region computed always contains the region that
1558 * would have been computed without this directive, so no
1559 * assertions are injected.
1560 */
1561 Func &align_bounds(const Var &var, Expr modulus, Expr remainder = 0);
1562
1563 /** Expand the region computed so that the extent is a
1564 * multiple of 'modulus'. For example, f.align_extent(x, 2) forces
1565 * the extent realized to be even. The region computed always contains the
1566 * region that would have been computed without this directive, so no
1567 * assertions are injected. (This is essentially equivalent to align_bounds(),
1568 * but always leaving the min untouched.)
1569 */
1570 Func &align_extent(const Var &var, Expr modulus);
1571
1572 /** Bound the extent of a Func's realization, but not its
1573 * min. This means the dimension can be unrolled or vectorized
1574 * even when its min is not fixed (for example because it is
1575 * compute_at tiles of another Func). This can also be useful for
1576 * forcing a function's allocation to be a fixed size, which often
1577 * means it can go on the stack. */
1578 Func &bound_extent(const Var &var, Expr extent);
1579
1580 /** Split two dimensions at once by the given factors, and then
1581 * reorder the resulting dimensions to be xi, yi, xo, yo from
1582 * innermost outwards. This gives a tiled traversal. */
1583 Func &tile(const VarOrRVar &x, const VarOrRVar &y,
1584 const VarOrRVar &xo, const VarOrRVar &yo,
1585 const VarOrRVar &xi, const VarOrRVar &yi,
1586 const Expr &xfactor, const Expr &yfactor,
1587 TailStrategy tail = TailStrategy::Auto);
1588
1589 /** A shorter form of tile, which reuses the old variable names as
1590 * the new outer dimensions */
1591 Func &tile(const VarOrRVar &x, const VarOrRVar &y,
1592 const VarOrRVar &xi, const VarOrRVar &yi,
1593 const Expr &xfactor, const Expr &yfactor,
1594 TailStrategy tail = TailStrategy::Auto);
1595
1596 /** A more general form of tile, which defines tiles of any dimensionality. */
1597 Func &tile(const std::vector<VarOrRVar> &previous,
1598 const std::vector<VarOrRVar> &outers,
1599 const std::vector<VarOrRVar> &inners,
1600 const std::vector<Expr> &factors,
1601 const std::vector<TailStrategy> &tails);
1602
1603 /** The generalized tile, with a single tail strategy to apply to all vars. */
1604 Func &tile(const std::vector<VarOrRVar> &previous,
1605 const std::vector<VarOrRVar> &outers,
1606 const std::vector<VarOrRVar> &inners,
1607 const std::vector<Expr> &factors,
1608 TailStrategy tail = TailStrategy::Auto);
1609
1610 /** Generalized tiling, reusing the previous names as the outer names. */
1611 Func &tile(const std::vector<VarOrRVar> &previous,
1612 const std::vector<VarOrRVar> &inners,
1613 const std::vector<Expr> &factors,
1614 TailStrategy tail = TailStrategy::Auto);
1615
1616 /** Reorder variables to have the given nesting order, from
1617 * innermost out */
1618 Func &reorder(const std::vector<VarOrRVar> &vars);
1619
1620 template<typename... Args>
1621 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<VarOrRVar, Args...>::value, Func &>
1622 reorder(const VarOrRVar &x, const VarOrRVar &y, Args &&...args) {
1623 std::vector<VarOrRVar> collected_args{x, y, std::forward<Args>(args)...};
1624 return reorder(collected_args);
1625 }
1626
1627 /** Get the Vars of the pure definition, with splits applied. This
1628 * represents all the potentially-valid compute_at sites for this stage of
1629 * this Func. Note that this, by default, will end with the sentinel
1630 * Var::outermost. */
1631 std::vector<Var> split_vars() const;
1632
1633 /** Rename a dimension. Equivalent to split with a inner size of one. */
1634 Func &rename(const VarOrRVar &old_name, const VarOrRVar &new_name);
1635
1636 /** Specify that race conditions are permitted for this Func,
1637 * which enables parallelizing over RVars even when Halide cannot
1638 * prove that it is safe to do so. Use this with great caution,
1639 * and only if you can prove to yourself that this is safe, as it
1640 * may result in a non-deterministic routine that returns
1641 * different values at different times or on different machines. */
1642 Func &allow_race_conditions();
1643
1644 /** Issue atomic updates for this Func. This allows parallelization
1645 * on associative RVars. The function throws a compile error when
1646 * Halide fails to prove associativity. Use override_associativity_test
1647 * to disable the associativity test if you believe the function is
1648 * associative or the order of reduction variable execution does not
1649 * matter.
1650 * Halide compiles this into hardware atomic operations whenever possible,
1651 * and falls back to a mutex lock per storage element if it is impossible
1652 * to atomically update.
1653 * There are three possible outcomes of the compiled code:
1654 * atomic add, compare-and-swap loop, and mutex lock.
1655 * For example:
1656 *
1657 * hist(x) = 0;
1658 * hist(im(r)) += 1;
1659 * hist.compute_root();
1660 * hist.update().atomic().parallel();
1661 *
1662 * will be compiled to atomic add operations.
1663 *
1664 * hist(x) = 0;
1665 * hist(im(r)) = min(hist(im(r)) + 1, 100);
1666 * hist.compute_root();
1667 * hist.update().atomic().parallel();
1668 *
1669 * will be compiled to compare-and-swap loops.
1670 *
1671 * arg_max() = {0, im(0)};
1672 * Expr old_index = arg_max()[0];
1673 * Expr old_max = arg_max()[1];
1674 * Expr new_index = select(old_max < im(r), r, old_index);
1675 * Expr new_max = max(im(r), old_max);
1676 * arg_max() = {new_index, new_max};
1677 * arg_max.compute_root();
1678 * arg_max.update().atomic().parallel();
1679 *
1680 * will be compiled to updates guarded by a mutex lock,
1681 * since it is impossible to atomically update two different locations.
1682 *
1683 * Currently the atomic operation is supported by x86, CUDA, and OpenCL backends.
1684 * Compiling to other backends results in a compile error.
1685 * If an operation is compiled into a mutex lock, and is vectorized or is
1686 * compiled to CUDA or OpenCL, it also results in a compile error,
1687 * since per-element mutex lock on vectorized operation leads to a
1688 * deadlock.
1689 * Vectorization of predicated RVars (through rdom.where()) on CPU
1690 * is also unsupported yet (see https://github.com/halide/Halide/issues/4298).
1691 * 8-bit and 16-bit atomics on GPU are also not supported. */
1692 Func &atomic(bool override_associativity_test = false);
1693
1694 /** Specialize a Func. This creates a special-case version of the
1695 * Func where the given condition is true. The most effective
1696 * conditions are those of the form param == value, and boolean
1697 * Params. Consider a simple example:
1698 \code
1699 f(x) = x + select(cond, 0, 1);
1700 f.compute_root();
1701 \endcode
1702 * This is equivalent to:
1703 \code
1704 for (int x = 0; x < width; x++) {
1705 f[x] = x + (cond ? 0 : 1);
1706 }
1707 \endcode
1708 * Adding the scheduling directive:
1709 \code
1710 f.specialize(cond)
1711 \endcode
1712 * makes it equivalent to:
1713 \code
1714 if (cond) {
1715 for (int x = 0; x < width; x++) {
1716 f[x] = x;
1717 }
1718 } else {
1719 for (int x = 0; x < width; x++) {
1720 f[x] = x + 1;
1721 }
1722 }
1723 \endcode
1724 * Note that the inner loops have been simplified. In the first
1725 * path Halide knows that cond is true, and in the second path
1726 * Halide knows that it is false.
1727 *
1728 * The specialized version gets its own schedule, which inherits
1729 * every directive made about the parent Func's schedule so far
1730 * except for its specializations. This method returns a handle to
1731 * the new schedule. If you wish to retrieve the specialized
1732 * sub-schedule again later, you can call this method with the
1733 * same condition. Consider the following example of scheduling
1734 * the specialized version:
1735 *
1736 \code
1737 f(x) = x;
1738 f.compute_root();
1739 f.specialize(width > 1).unroll(x, 2);
1740 \endcode
1741 * Assuming for simplicity that width is even, this is equivalent to:
1742 \code
1743 if (width > 1) {
1744 for (int x = 0; x < width/2; x++) {
1745 f[2*x] = 2*x;
1746 f[2*x + 1] = 2*x + 1;
1747 }
1748 } else {
1749 for (int x = 0; x < width/2; x++) {
1750 f[x] = x;
1751 }
1752 }
1753 \endcode
1754 * For this case, it may be better to schedule the un-specialized
1755 * case instead:
1756 \code
1757 f(x) = x;
1758 f.compute_root();
1759 f.specialize(width == 1); // Creates a copy of the schedule so far.
1760 f.unroll(x, 2); // Only applies to the unspecialized case.
1761 \endcode
1762 * This is equivalent to:
1763 \code
1764 if (width == 1) {
1765 f[0] = 0;
1766 } else {
1767 for (int x = 0; x < width/2; x++) {
1768 f[2*x] = 2*x;
1769 f[2*x + 1] = 2*x + 1;
1770 }
1771 }
1772 \endcode
1773 * This can be a good way to write a pipeline that splits,
1774 * vectorizes, or tiles, but can still handle small inputs.
1775 *
1776 * If a Func has several specializations, the first matching one
1777 * will be used, so the order in which you define specializations
1778 * is significant. For example:
1779 *
1780 \code
1781 f(x) = x + select(cond1, a, b) - select(cond2, c, d);
1782 f.specialize(cond1);
1783 f.specialize(cond2);
1784 \endcode
1785 * is equivalent to:
1786 \code
1787 if (cond1) {
1788 for (int x = 0; x < width; x++) {
1789 f[x] = x + a - (cond2 ? c : d);
1790 }
1791 } else if (cond2) {
1792 for (int x = 0; x < width; x++) {
1793 f[x] = x + b - c;
1794 }
1795 } else {
1796 for (int x = 0; x < width; x++) {
1797 f[x] = x + b - d;
1798 }
1799 }
1800 \endcode
1801 *
1802 * Specializations may in turn be specialized, which creates a
1803 * nested if statement in the generated code.
1804 *
1805 \code
1806 f(x) = x + select(cond1, a, b) - select(cond2, c, d);
1807 f.specialize(cond1).specialize(cond2);
1808 \endcode
1809 * This is equivalent to:
1810 \code
1811 if (cond1) {
1812 if (cond2) {
1813 for (int x = 0; x < width; x++) {
1814 f[x] = x + a - c;
1815 }
1816 } else {
1817 for (int x = 0; x < width; x++) {
1818 f[x] = x + a - d;
1819 }
1820 }
1821 } else {
1822 for (int x = 0; x < width; x++) {
1823 f[x] = x + b - (cond2 ? c : d);
1824 }
1825 }
1826 \endcode
1827 * To create a 4-way if statement that simplifies away all of the
1828 * ternary operators above, you could say:
1829 \code
1830 f.specialize(cond1).specialize(cond2);
1831 f.specialize(cond2);
1832 \endcode
1833 * or
1834 \code
1835 f.specialize(cond1 && cond2);
1836 f.specialize(cond1);
1837 f.specialize(cond2);
1838 \endcode
1839 *
1840 * Any prior Func which is compute_at some variable of this Func
1841 * gets separately included in all paths of the generated if
1842 * statement. The Var in the compute_at call to must exist in all
1843 * paths, but it may have been generated via a different path of
1844 * splits, fuses, and renames. This can be used somewhat
1845 * creatively. Consider the following code:
1846 \code
1847 g(x, y) = 8*x;
1848 f(x, y) = g(x, y) + 1;
1849 f.compute_root().specialize(cond);
1850 Var g_loop;
1851 f.specialize(cond).rename(y, g_loop);
1852 f.rename(x, g_loop);
1853 g.compute_at(f, g_loop);
1854 \endcode
1855 * When cond is true, this is equivalent to g.compute_at(f,y).
1856 * When it is false, this is equivalent to g.compute_at(f,x).
1857 */
1858 Stage specialize(const Expr &condition);
1859
1860 /** Add a specialization to a Func that always terminates execution
1861 * with a call to halide_error(). By itself, this is of limited use,
1862 * but can be useful to terminate chains of specialize() calls where
1863 * no "default" case is expected (thus avoiding unnecessary code generation).
1864 *
1865 * For instance, say we want to optimize a pipeline to process images
1866 * in planar and interleaved format; we might typically do something like:
1867 \code
1868 ImageParam im(UInt(8), 3);
1869 Func f = do_something_with(im);
1870 f.specialize(im.dim(0).stride() == 1).vectorize(x, 8); // planar
1871 f.specialize(im.dim(2).stride() == 1).reorder(c, x, y).vectorize(c); // interleaved
1872 \endcode
1873 * This code will vectorize along rows for the planar case, and across pixel
1874 * components for the interleaved case... but there is an implicit "else"
1875 * for the unhandled cases, which generates unoptimized code. If we never
1876 * anticipate passing any other sort of images to this, we code streamline
1877 * our code by adding specialize_fail():
1878 \code
1879 ImageParam im(UInt(8), 3);
1880 Func f = do_something(im);
1881 f.specialize(im.dim(0).stride() == 1).vectorize(x, 8); // planar
1882 f.specialize(im.dim(2).stride() == 1).reorder(c, x, y).vectorize(c); // interleaved
1883 f.specialize_fail("Unhandled image format");
1884 \endcode
1885 * Conceptually, this produces codes like:
1886 \code
1887 if (im.dim(0).stride() == 1) {
1888 do_something_planar();
1889 } else if (im.dim(2).stride() == 1) {
1890 do_something_interleaved();
1891 } else {
1892 halide_error("Unhandled image format");
1893 }
1894 \endcode
1895 *
1896 * Note that calling specialize_fail() terminates the specialization chain
1897 * for a given Func; you cannot create new specializations for the Func
1898 * afterwards (though you can retrieve handles to previous specializations).
1899 */
1900 void specialize_fail(const std::string &message);
1901
1902 /** Tell Halide that the following dimensions correspond to GPU
1903 * thread indices. This is useful if you compute a producer
1904 * function within the block indices of a consumer function, and
1905 * want to control how that function's dimensions map to GPU
1906 * threads. If the selected target is not an appropriate GPU, this
1907 * just marks those dimensions as parallel. */
1908 // @{
1909 Func &gpu_threads(const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
1910 Func &gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api = DeviceAPI::Default_GPU);
1911 Func &gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api = DeviceAPI::Default_GPU);
1912 // @}
1913
1914 /** The given dimension corresponds to the lanes in a GPU
1915 * warp. GPU warp lanes are distinguished from GPU threads by the
1916 * fact that all warp lanes run together in lockstep, which
1917 * permits lightweight communication of data from one lane to
1918 * another. */
1919 Func &gpu_lanes(const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
1920
1921 /** Tell Halide to run this stage using a single gpu thread and
1922 * block. This is not an efficient use of your GPU, but it can be
1923 * useful to avoid copy-back for intermediate update stages that
1924 * touch a very small part of your Func. */
1925 Func &gpu_single_thread(DeviceAPI device_api = DeviceAPI::Default_GPU);
1926
1927 /** Tell Halide that the following dimensions correspond to GPU
1928 * block indices. This is useful for scheduling stages that will
1929 * run serially within each GPU block. If the selected target is
1930 * not ptx, this just marks those dimensions as parallel. */
1931 // @{
1932 Func &gpu_blocks(const VarOrRVar &block_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
1933 Func &gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, DeviceAPI device_api = DeviceAPI::Default_GPU);
1934 Func &gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, DeviceAPI device_api = DeviceAPI::Default_GPU);
1935 // @}
1936
1937 /** Tell Halide that the following dimensions correspond to GPU
1938 * block indices and thread indices. If the selected target is not
1939 * ptx, these just mark the given dimensions as parallel. The
1940 * dimensions are consumed by this call, so do all other
1941 * unrolling, reordering, etc first. */
1942 // @{
1943 Func &gpu(const VarOrRVar &block_x, const VarOrRVar &thread_x, DeviceAPI device_api = DeviceAPI::Default_GPU);
1944 Func &gpu(const VarOrRVar &block_x, const VarOrRVar &block_y,
1945 const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api = DeviceAPI::Default_GPU);
1946 Func &gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z,
1947 const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api = DeviceAPI::Default_GPU);
1948 // @}
1949
1950 /** Short-hand for tiling a domain and mapping the tile indices
1951 * to GPU block indices and the coordinates within each tile to
1952 * GPU thread indices. Consumes the variables given, so do all
1953 * other scheduling first. */
1954 // @{
1955 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &bx, const VarOrRVar &tx, const Expr &x_size,
1956 TailStrategy tail = TailStrategy::Auto,
1957 DeviceAPI device_api = DeviceAPI::Default_GPU);
1958
1959 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &tx, const Expr &x_size,
1960 TailStrategy tail = TailStrategy::Auto,
1961 DeviceAPI device_api = DeviceAPI::Default_GPU);
1962 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &y,
1963 const VarOrRVar &bx, const VarOrRVar &by,
1964 const VarOrRVar &tx, const VarOrRVar &ty,
1965 const Expr &x_size, const Expr &y_size,
1966 TailStrategy tail = TailStrategy::Auto,
1967 DeviceAPI device_api = DeviceAPI::Default_GPU);
1968
1969 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &y,
1970 const VarOrRVar &tx, const VarOrRVar &ty,
1971 const Expr &x_size, const Expr &y_size,
1972 TailStrategy tail = TailStrategy::Auto,
1973 DeviceAPI device_api = DeviceAPI::Default_GPU);
1974
1975 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z,
1976 const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &bz,
1977 const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz,
1978 const Expr &x_size, const Expr &y_size, const Expr &z_size,
1979 TailStrategy tail = TailStrategy::Auto,
1980 DeviceAPI device_api = DeviceAPI::Default_GPU);
1981 Func &gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z,
1982 const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz,
1983 const Expr &x_size, const Expr &y_size, const Expr &z_size,
1984 TailStrategy tail = TailStrategy::Auto,
1985 DeviceAPI device_api = DeviceAPI::Default_GPU);
1986 // @}
1987
1988 /** Schedule for execution on Hexagon. When a loop is marked with
1989 * Hexagon, that loop is executed on a Hexagon DSP. */
1990 Func &hexagon(const VarOrRVar &x = Var::outermost());
1991
1992 /** Prefetch data written to or read from a Func or an ImageParam by a
1993 * subsequent loop iteration, at an optionally specified iteration offset. You may specify
1994 * specification of different vars for the location of the prefetch() instruction
1995 * vs. the location that is being prefetched:
1996 *
1997 * - the first var specified, 'at', indicates the loop in which the prefetch will be placed
1998 * - the second var specified, 'from', determines the var used to find the bounds to prefetch
1999 * (in conjunction with 'offset')
2000 *
2001 * If 'at' and 'from' are distinct vars, then 'from' must be at a nesting level outside 'at.'
2002 * Note that the value for 'offset' applies only to 'from', not 'at'.
2003 *
2004 * The final argument specifies how prefetch of region outside bounds
2005 * should be handled.
2006 *
2007 * For example, consider this pipeline:
2008 \code
2009 Func f, g;
2010 Var x, y, z;
2011 f(x, y) = x + y;
2012 g(x, y) = 2 * f(x, y);
2013 h(x, y) = 3 * f(x, y);
2014 \endcode
2015 *
2016 * The following schedule:
2017 \code
2018 f.compute_root();
2019 g.prefetch(f, x, x, 2, PrefetchBoundStrategy::NonFaulting);
2020 h.prefetch(f, x, y, 2, PrefetchBoundStrategy::NonFaulting);
2021 \endcode
2022 *
2023 * will inject prefetch call at the innermost loop of 'g' and 'h' and generate
2024 * the following loop nest:
2025 \code
2026 for y = ...
2027 for x = ...
2028 f(x, y) = x + y
2029 for y = ..
2030 for x = ...
2031 prefetch(&f[x + 2, y], 1, 16);
2032 g(x, y) = 2 * f(x, y)
2033 for y = ..
2034 for x = ...
2035 prefetch(&f[x, y + 2], 1, 16);
2036 h(x, y) = 3 * f(x, y)
2037 \endcode
2038 *
2039 * Note that the 'from' nesting level need not be adjacent to 'at':
2040 \code
2041 Func f, g;
2042 Var x, y, z, w;
2043 f(x, y, z, w) = x + y + z + w;
2044 g(x, y, z, w) = 2 * f(x, y, z, w);
2045 \endcode
2046 *
2047 * The following schedule:
2048 \code
2049 f.compute_root();
2050 g.prefetch(f, y, w, 2, PrefetchBoundStrategy::NonFaulting);
2051 \endcode
2052 *
2053 * will produce code that prefetches a tile of data:
2054 \code
2055 for w = ...
2056 for z = ...
2057 for y = ...
2058 for x = ...
2059 f(x, y, z, w) = x + y + z + w
2060 for w = ...
2061 for z = ...
2062 for y = ...
2063 for x0 = ...
2064 prefetch(&f[x0, y, z, w + 2], 1, 16);
2065 for x = ...
2066 g(x, y, z, w) = 2 * f(x, y, z, w)
2067 \endcode
2068 *
2069 * Note that calling prefetch() with the same var for both 'at' and 'from'
2070 * is equivalent to calling prefetch() with that var.
2071 */
2072 // @{
2073 Func &prefetch(const Func &f, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
2074 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf);
2075 Func &prefetch(const Parameter &param, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
2076 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf);
2077 template<typename T>
2078 Func &prefetch(const T &image, const VarOrRVar &at, const VarOrRVar &from, Expr offset = 1,
2079 PrefetchBoundStrategy strategy = PrefetchBoundStrategy::GuardWithIf) {
2080 return prefetch(image.parameter(), at, from, std::move(offset), strategy);
2081 }
2082 // @}
2083
2084 /** Specify how the storage for the function is laid out. These
2085 * calls let you specify the nesting order of the dimensions. For
2086 * example, foo.reorder_storage(y, x) tells Halide to use
2087 * column-major storage for any realizations of foo, without
2088 * changing how you refer to foo in the code. You may want to do
2089 * this if you intend to vectorize across y. When representing
2090 * color images, foo.reorder_storage(c, x, y) specifies packed
2091 * storage (red, green, and blue values adjacent in memory), and
2092 * foo.reorder_storage(x, y, c) specifies planar storage (entire
2093 * red, green, and blue images one after the other in memory).
2094 *
2095 * If you leave out some dimensions, those remain in the same
2096 * positions in the nesting order while the specified variables
2097 * are reordered around them. */
2098 // @{
2099 Func &reorder_storage(const std::vector<Var> &dims);
2100
2101 Func &reorder_storage(const Var &x, const Var &y);
2102 template<typename... Args>
2103 HALIDE_NO_USER_CODE_INLINE std::enable_if_t<Internal::all_are_convertible<Var, Args...>::value, Func &>
2104 reorder_storage(const Var &x, const Var &y, Args &&...args) {
2105 std::vector<Var> collected_args{x, y, std::forward<Args>(args)...};
2106 return reorder_storage(collected_args);
2107 }
2108 // @}
2109
2110 /** Pad the storage extent of a particular dimension of
2111 * realizations of this function up to be a multiple of the
2112 * specified alignment. This guarantees that the strides for the
2113 * dimensions stored outside of dim will be multiples of the
2114 * specified alignment, where the strides and alignment are
2115 * measured in numbers of elements.
2116 *
2117 * For example, to guarantee that a function foo(x, y, c)
2118 * representing an image has scanlines starting on offsets
2119 * aligned to multiples of 16, use foo.align_storage(x, 16). */
2120 Func &align_storage(const Var &dim, const Expr &alignment);
2121
2122 /** Store realizations of this function in a circular buffer of a
2123 * given extent. This is more efficient when the extent of the
2124 * circular buffer is a power of 2. If the fold factor is too
2125 * small, or the dimension is not accessed monotonically, the
2126 * pipeline will generate an error at runtime.
2127 *
2128 * The fold_forward option indicates that the new values of the
2129 * producer are accessed by the consumer in a monotonically
2130 * increasing order. Folding storage of producers is also
2131 * supported if the new values are accessed in a monotonically
2132 * decreasing order by setting fold_forward to false.
2133 *
2134 * For example, consider the pipeline:
2135 \code
2136 Func f, g;
2137 Var x, y;
2138 g(x, y) = x*y;
2139 f(x, y) = g(x, y) + g(x, y+1);
2140 \endcode
2141 *
2142 * If we schedule f like so:
2143 *
2144 \code
2145 g.compute_at(f, y).store_root().fold_storage(y, 2);
2146 \endcode
2147 *
2148 * Then g will be computed at each row of f and stored in a buffer
2149 * with an extent in y of 2, alternately storing each computed row
2150 * of g in row y=0 or y=1.
2151 */
2152 Func &fold_storage(const Var &dim, const Expr &extent, bool fold_forward = true);
2153
2154 /** Compute this function as needed for each unique value of the
2155 * given var for the given calling function f.
2156 *
2157 * For example, consider the simple pipeline:
2158 \code
2159 Func f, g;
2160 Var x, y;
2161 g(x, y) = x*y;
2162 f(x, y) = g(x, y) + g(x, y+1) + g(x+1, y) + g(x+1, y+1);
2163 \endcode
2164 *
2165 * If we schedule f like so:
2166 *
2167 \code
2168 g.compute_at(f, x);
2169 \endcode
2170 *
2171 * Then the C code equivalent to this pipeline will look like this
2172 *
2173 \code
2174
2175 int f[height][width];
2176 for (int y = 0; y < height; y++) {
2177 for (int x = 0; x < width; x++) {
2178 int g[2][2];
2179 g[0][0] = x*y;
2180 g[0][1] = (x+1)*y;
2181 g[1][0] = x*(y+1);
2182 g[1][1] = (x+1)*(y+1);
2183 f[y][x] = g[0][0] + g[1][0] + g[0][1] + g[1][1];
2184 }
2185 }
2186
2187 \endcode
2188 *
2189 * The allocation and computation of g is within f's loop over x,
2190 * and enough of g is computed to satisfy all that f will need for
2191 * that iteration. This has excellent locality - values of g are
2192 * used as soon as they are computed, but it does redundant
2193 * work. Each value of g ends up getting computed four times. If
2194 * we instead schedule f like so:
2195 *
2196 \code
2197 g.compute_at(f, y);
2198 \endcode
2199 *
2200 * The equivalent C code is:
2201 *
2202 \code
2203 int f[height][width];
2204 for (int y = 0; y < height; y++) {
2205 int g[2][width+1];
2206 for (int x = 0; x < width; x++) {
2207 g[0][x] = x*y;
2208 g[1][x] = x*(y+1);
2209 }
2210 for (int x = 0; x < width; x++) {
2211 f[y][x] = g[0][x] + g[1][x] + g[0][x+1] + g[1][x+1];
2212 }
2213 }
2214 \endcode
2215 *
2216 * The allocation and computation of g is within f's loop over y,
2217 * and enough of g is computed to satisfy all that f will need for
2218 * that iteration. This does less redundant work (each point in g
2219 * ends up being evaluated twice), but the locality is not quite
2220 * as good, and we have to allocate more temporary memory to store
2221 * g.
2222 */
2223 Func &compute_at(const Func &f, const Var &var);
2224
2225 /** Schedule a function to be computed within the iteration over
2226 * some dimension of an update domain. Produces equivalent code
2227 * to the version of compute_at that takes a Var. */
2228 Func &compute_at(const Func &f, const RVar &var);
2229
2230 /** Schedule a function to be computed within the iteration over
2231 * a given LoopLevel. */
2232 Func &compute_at(LoopLevel loop_level);
2233
2234 /** Schedule the iteration over the initial definition of this function
2235 * to be fused with another stage 's' from outermost loop to a
2236 * given LoopLevel. */
2237 // @{
2238 Func &compute_with(const Stage &s, const VarOrRVar &var, const std::vector<std::pair<VarOrRVar, LoopAlignStrategy>> &align);
2239 Func &compute_with(const Stage &s, const VarOrRVar &var, LoopAlignStrategy align = LoopAlignStrategy::Auto);
2240 Func &compute_with(LoopLevel loop_level, const std::vector<std::pair<VarOrRVar, LoopAlignStrategy>> &align);
2241 Func &compute_with(LoopLevel loop_level, LoopAlignStrategy align = LoopAlignStrategy::Auto);
2242
2243 /** Compute all of this function once ahead of time. Reusing
2244 * the example in \ref Func::compute_at :
2245 *
2246 \code
2247 Func f, g;
2248 Var x, y;
2249 g(x, y) = x*y;
2250 f(x, y) = g(x, y) + g(x, y+1) + g(x+1, y) + g(x+1, y+1);
2251
2252 g.compute_root();
2253 \endcode
2254 *
2255 * is equivalent to
2256 *
2257 \code
2258 int f[height][width];
2259 int g[height+1][width+1];
2260 for (int y = 0; y < height+1; y++) {
2261 for (int x = 0; x < width+1; x++) {
2262 g[y][x] = x*y;
2263 }
2264 }
2265 for (int y = 0; y < height; y++) {
2266 for (int x = 0; x < width; x++) {
2267 f[y][x] = g[y][x] + g[y+1][x] + g[y][x+1] + g[y+1][x+1];
2268 }
2269 }
2270 \endcode
2271 *
2272 * g is computed once ahead of time, and enough is computed to
2273 * satisfy all uses of it. This does no redundant work (each point
2274 * in g is evaluated once), but has poor locality (values of g are
2275 * probably not still in cache when they are used by f), and
2276 * allocates lots of temporary memory to store g.
2277 */
2278 Func &compute_root();
2279
2280 /** Use the halide_memoization_cache_... interface to store a
2281 * computed version of this function across invocations of the
2282 * Func.
2283 *
2284 * If an eviction_key is provided, it must be constructed with
2285 * Expr of integer or handle type. The key Expr will be promoted
2286 * to a uint64_t and can be used with halide_memoization_cache_evict
2287 * to remove memoized entries using this eviction key from the
2288 * cache. Memoized computations that do not provide an eviction
2289 * key will never be evicted by this mechanism.
2290 *
2291 * It is invalid to memoize the output of a Pipeline; attempting
2292 * to do so will issue an error. To cache an entire pipeline,
2293 * either implement a caching mechanism outside of Halide or
2294 * explicitly copy out of the cache with another output Func.
2295 */
2296 Func &memoize(const EvictionKey &eviction_key = EvictionKey());
2297
2298 /** Produce this Func asynchronously in a separate
2299 * thread. Consumers will be run by the task system when the
2300 * production is complete. If this Func's store level is different
2301 * to its compute level, consumers will be run concurrently,
2302 * blocking as necessary to prevent reading ahead of what the
2303 * producer has computed. If storage is folded, then the producer
2304 * will additionally not be permitted to run too far ahead of the
2305 * consumer, to avoid clobbering data that has not yet been
2306 * used.
2307 *
2308 * Take special care when combining this with custom thread pool
2309 * implementations, as avoiding deadlock with producer-consumer
2310 * parallelism requires a much more sophisticated parallel runtime
2311 * than with data parallelism alone. It is strongly recommended
2312 * you just use Halide's default thread pool, which guarantees no
2313 * deadlock and a bound on the number of threads launched.
2314 */
2315 Func &async();
2316
2317 /** Expands the storage of the function by an extra dimension
2318 * to enable ring buffering. For this to be useful the storage
2319 * of the function has to be hoisted to an upper loop level using
2320 * \ref Func::hoist_storage. The index for the new ring buffer dimension
2321 * is calculated implicitly based on a linear combination of the all of
2322 * the loop variables between hoist_storage and compute_at/store_at
2323 * loop levels. Scheduling a function with ring_buffer increases the
2324 * amount of memory required for this function by an *extent* times.
2325 * ring_buffer is especially useful in combination with \ref Func::async,
2326 * but can be used without it.
2327 *
2328 * The extent is expected to be a positive integer.
2329 */
2330 Func &ring_buffer(Expr extent);
2331
2332 /** Bound the extent of a Func's storage, but not extent of its
2333 * compute. This can be useful for forcing a function's allocation
2334 * to be a fixed size, which often means it can go on the stack.
2335 * If bounds inference decides that it requires more storage for
2336 * this function than the allocation size you have stated, a runtime
2337 * error will occur when you try to run the pipeline. */
2338 Func &bound_storage(const Var &dim, const Expr &bound);
2339
2340 /** Allocate storage for this function within f's loop over
2341 * var. Scheduling storage is optional, and can be used to
2342 * separate the loop level at which storage occurs from the loop
2343 * level at which computation occurs to trade off between locality
2344 * and redundant work. This can open the door for two types of
2345 * optimization.
2346 *
2347 * Consider again the pipeline from \ref Func::compute_at :
2348 \code
2349 Func f, g;
2350 Var x, y;
2351 g(x, y) = x*y;
2352 f(x, y) = g(x, y) + g(x+1, y) + g(x, y+1) + g(x+1, y+1);
2353 \endcode
2354 *
2355 * If we schedule it like so:
2356 *
2357 \code
2358 g.compute_at(f, x).store_at(f, y);
2359 \endcode
2360 *
2361 * Then the computation of g takes place within the loop over x,
2362 * but the storage takes place within the loop over y:
2363 *
2364 \code
2365 int f[height][width];
2366 for (int y = 0; y < height; y++) {
2367 int g[2][width+1];
2368 for (int x = 0; x < width; x++) {
2369 g[0][x] = x*y;
2370 g[0][x+1] = (x+1)*y;
2371 g[1][x] = x*(y+1);
2372 g[1][x+1] = (x+1)*(y+1);
2373 f[y][x] = g[0][x] + g[1][x] + g[0][x+1] + g[1][x+1];
2374 }
2375 }
2376 \endcode
2377 *
2378 * Provided the for loop over x is serial, halide then
2379 * automatically performs the following sliding window
2380 * optimization:
2381 *
2382 \code
2383 int f[height][width];
2384 for (int y = 0; y < height; y++) {
2385 int g[2][width+1];
2386 for (int x = 0; x < width; x++) {
2387 if (x == 0) {
2388 g[0][x] = x*y;
2389 g[1][x] = x*(y+1);
2390 }
2391 g[0][x+1] = (x+1)*y;
2392 g[1][x+1] = (x+1)*(y+1);
2393 f[y][x] = g[0][x] + g[1][x] + g[0][x+1] + g[1][x+1];
2394 }
2395 }
2396 \endcode
2397 *
2398 * Two of the assignments to g only need to be done when x is
2399 * zero. The rest of the time, those sites have already been
2400 * filled in by a previous iteration. This version has the
2401 * locality of compute_at(f, x), but allocates more memory and
2402 * does much less redundant work.
2403 *
2404 * Halide then further optimizes this pipeline like so:
2405 *
2406 \code
2407 int f[height][width];
2408 for (int y = 0; y < height; y++) {
2409 int g[2][2];
2410 for (int x = 0; x < width; x++) {
2411 if (x == 0) {
2412 g[0][0] = x*y;
2413 g[1][0] = x*(y+1);
2414 }
2415 g[0][(x+1)%2] = (x+1)*y;
2416 g[1][(x+1)%2] = (x+1)*(y+1);
2417 f[y][x] = g[0][x%2] + g[1][x%2] + g[0][(x+1)%2] + g[1][(x+1)%2];
2418 }
2419 }
2420 \endcode
2421 *
2422 * Halide has detected that it's possible to use a circular buffer
2423 * to represent g, and has reduced all accesses to g modulo 2 in
2424 * the x dimension. This optimization only triggers if the for
2425 * loop over x is serial, and if halide can statically determine
2426 * some power of two large enough to cover the range needed. For
2427 * powers of two, the modulo operator compiles to more efficient
2428 * bit-masking. This optimization reduces memory usage, and also
2429 * improves locality by reusing recently-accessed memory instead
2430 * of pulling new memory into cache.
2431 *
2432 */
2433 Func &store_at(const Func &f, const Var &var);
2434
2435 /** Equivalent to the version of store_at that takes a Var, but
2436 * schedules storage within the loop over a dimension of a
2437 * reduction domain */
2438 Func &store_at(const Func &f, const RVar &var);
2439
2440 /** Equivalent to the version of store_at that takes a Var, but
2441 * schedules storage at a given LoopLevel. */
2442 Func &store_at(LoopLevel loop_level);
2443
2444 /** Equivalent to \ref Func::store_at, but schedules storage
2445 * outside the outermost loop. */
2446 Func &store_root();
2447
2448 /** Hoist storage for this function within f's loop over
2449 * var. This is different from \ref Func::store_at, because hoist_storage
2450 * simply moves an actual allocation to a given loop level and
2451 * doesn't trigger any of the optimizations such as sliding window.
2452 * Hoisting storage is optional and can be used as an optimization
2453 * to avoid unnecessary allocations by moving it out from an inner
2454 * loop.
2455 *
2456 * Consider again the pipeline from \ref Func::compute_at :
2457 \code
2458 Func f, g;
2459 Var x, y;
2460 g(x, y) = x*y;
2461 f(x, y) = g(x, y) + g(x, y+1) + g(x+1, y) + g(x+1, y+1);
2462 \endcode
2463 *
2464 * If we schedule f like so:
2465 *
2466 \code
2467 g.compute_at(f, x);
2468 \endcode
2469 *
2470 * Then the C code equivalent to this pipeline will look like this
2471 *
2472 \code
2473
2474 int f[height][width];
2475 for (int y = 0; y < height; y++) {
2476 for (int x = 0; x < width; x++) {
2477 int g[2][2];
2478 g[0][0] = x*y;
2479 g[0][1] = (x+1)*y;
2480 g[1][0] = x*(y+1);
2481 g[1][1] = (x+1)*(y+1);
2482 f[y][x] = g[0][0] + g[1][0] + g[0][1] + g[1][1];
2483 }
2484 }
2485
2486 \endcode
2487 *
2488 * Note the allocation for g inside of the loop over variable x which
2489 * can happen for each iteration of the inner loop (in total height * width times).
2490 * In some cases allocation can be expensive, so it might be better to do it once
2491 * and reuse allocated memory across all iterations of the loop.
2492 *
2493 * This can be done by scheduling g like so:
2494 *
2495 \code
2496 g.compute_at(f, x).hoist_storage(f, Var::outermost());
2497 \endcode
2498 *
2499 * Then the C code equivalent to this pipeline will look like this
2500 *
2501 \code
2502
2503 int f[height][width];
2504 int g[2][2];
2505 for (int y = 0; y < height; y++) {
2506 for (int x = 0; x < width; x++) {
2507 g[0][0] = x*y;
2508 g[0][1] = (x+1)*y;
2509 g[1][0] = x*(y+1);
2510 g[1][1] = (x+1)*(y+1);
2511 f[y][x] = g[0][0] + g[1][0] + g[0][1] + g[1][1];
2512 }
2513 }
2514
2515 \endcode
2516 *
2517 * hoist_storage can be used together with \ref Func::store_at and
2518 * \ref Func::fold_storage (for example, to hoist the storage allocated
2519 * after sliding window optimization).
2520 *
2521 */
2522 Func &hoist_storage(const Func &f, const Var &var);
2523
2524 /** Equivalent to the version of hoist_storage that takes a Var, but
2525 * schedules storage within the loop over a dimension of a
2526 * reduction domain */
2527 Func &hoist_storage(const Func &f, const RVar &var);
2528
2529 /** Equivalent to the version of hoist_storage that takes a Var, but
2530 * schedules storage at a given LoopLevel. */
2531 Func &hoist_storage(LoopLevel loop_level);
2532
2533 /** Equivalent to \ref Func::hoist_storage_root, but schedules storage
2534 * outside the outermost loop. */
2535 Func &hoist_storage_root();
2536
2537 /** Aggressively inline all uses of this function. This is the
2538 * default schedule, so you're unlikely to need to call this. For
2539 * a Func with an update definition, that means it gets computed
2540 * as close to the innermost loop as possible.
2541 *
2542 * Consider once more the pipeline from \ref Func::compute_at :
2543 *
2544 \code
2545 Func f, g;
2546 Var x, y;
2547 g(x, y) = x*y;
2548 f(x, y) = g(x, y) + g(x+1, y) + g(x, y+1) + g(x+1, y+1);
2549 \endcode
2550 *
2551 * Leaving g as inline, this compiles to code equivalent to the following C:
2552 *
2553 \code
2554 int f[height][width];
2555 for (int y = 0; y < height; y++) {
2556 for (int x = 0; x < width; x++) {
2557 f[y][x] = x*y + x*(y+1) + (x+1)*y + (x+1)*(y+1);
2558 }
2559 }
2560 \endcode
2561 */
2562 Func &compute_inline();
2563
2564 /** Get a handle on an update step for the purposes of scheduling
2565 * it. */
2566 Stage update(int idx = 0);
2567
2568 /** Set the type of memory this Func should be stored in. Controls
2569 * whether allocations go on the stack or the heap on the CPU, and
2570 * in global vs shared vs local on the GPU. See the documentation
2571 * on MemoryType for more detail. */
2572 Func &store_in(MemoryType memory_type);
2573
2574 /** Trace all loads from this Func by emitting calls to
2575 * halide_trace. If the Func is inlined, this has no
2576 * effect. */
2577 Func &trace_loads();
2578
2579 /** Trace all stores to the buffer backing this Func by emitting
2580 * calls to halide_trace. If the Func is inlined, this call
2581 * has no effect. */
2582 Func &trace_stores();
2583
2584 /** Trace all realizations of this Func by emitting calls to
2585 * halide_trace. */
2586 Func &trace_realizations();
2587
2588 /** Add a string of arbitrary text that will be passed thru to trace
2589 * inspection code if the Func is realized in trace mode. (Funcs that are
2590 * inlined won't have their tags emitted.) Ignored entirely if
2591 * tracing is not enabled for the Func (or globally).
2592 */
2593 Func &add_trace_tag(const std::string &trace_tag);
2594
2595 /** Marks this function as a function that should not be profiled
2596 * when using the target feature Profile or ProfileByTimer.
2597 * This is useful when this function is does too little work at once
2598 * such that the overhead of setting the profiling token might
2599 * become significant, or that the measured time is not representative
2600 * due to modern processors (instruction level parallelism, out-of-order
2601 * execution). */
2602 Func &no_profiling();
2603
2604 /** Get a handle on the internal halide function that this Func
2605 * represents. Useful if you want to do introspection on Halide
2606 * functions */
2607 Internal::Function function() const {
2608 return func;
2609 }
2610
2611 /** You can cast a Func to its pure stage for the purposes of
2612 * scheduling it. */
2613 operator Stage() const;
2614
2615 /** Get a handle on the output buffer for this Func. Only relevant
2616 * if this is the output Func in a pipeline. Useful for making
2617 * static promises about strides, mins, and extents. */
2618 // @{
2619 OutputImageParam output_buffer() const;
2620 std::vector<OutputImageParam> output_buffers() const;
2621 // @}
2622
2623 /** Use a Func as an argument to an external stage. */
2624 operator ExternFuncArgument() const;
2625
2626 /** Infer the arguments to the Func, sorted into a canonical order:
2627 * all buffers (sorted alphabetically by name), followed by all non-buffers
2628 * (sorted alphabetically by name).
2629 This lets you write things like:
2630 \code
2631 func.compile_to_assembly("/dev/stdout", func.infer_arguments());
2632 \endcode
2633 */
2634 std::vector<Argument> infer_arguments() const;
2635
2636 /** Return the current StageSchedule associated with this initial
2637 * Stage of this Func. For introspection only: to modify schedule,
2638 * use the Func interface. */
2639 const Internal::StageSchedule &get_schedule() const {
2640 return Stage(*this).get_schedule();
2641 }
2642};
2643
2644 namespace Internal {
2645
2646 template<typename Last>
2647 inline void check_types(const Tuple &t, int idx) {
2648 using T = std::remove_pointer_t<std::remove_reference_t<Last>>;
2649 user_assert(t[idx].type() == type_of<T>())
2650 << "Can't evaluate expression "
2651 << t[idx] << " of type " << t[idx].type()
2652 << " as a scalar of type " << type_of<T>() << "\n";
2653}
2654
2655 template<typename First, typename Second, typename... Rest>
2656 inline void check_types(const Tuple &t, int idx) {
2657 check_types<First>(t, idx);
2658 check_types<Second, Rest...>(t, idx + 1);
2659}
2660
2661 template<typename Last>
2662 inline void assign_results(Realization &r, int idx, Last last) {
2663 using T = std::remove_pointer_t<std::remove_reference_t<Last>>;
2664 *last = Buffer<T>(r[idx])();
2665}
2666
2667 template<typename First, typename Second, typename... Rest>
2668 inline void assign_results(Realization &r, int idx, First first, Second second, Rest &&...rest) {
2669 assign_results<First>(r, idx, first);
2670 assign_results<Second, Rest...>(r, idx + 1, second, rest...);
2671}
2672
2673} // namespace Internal
2674
2675/** JIT-Compile and run enough code to evaluate a Halide
2676 * expression. This can be thought of as a scalar version of
2677 * \ref Func::realize */
2678 template<typename T>
2679 HALIDE_NO_USER_CODE_INLINE T evaluate(JITUserContext *ctx, const Expr &e) {
2680 user_assert(e.type() == type_of<T>())
2681 << "Can't evaluate expression "
2682 << e << " of type " << e.type()
2683 << " as a scalar of type " << type_of<T>() << "\n";
2684 Func f;
2685 f() = e;
2686 Buffer<T, 0> im = f.realize(ctx);
2687 return im();
2688}
2689
2690/** evaluate with a default user context */
2691 template<typename T>
2692 HALIDE_NO_USER_CODE_INLINE T evaluate(const Expr &e) {
2693 return evaluate<T>(nullptr, e);
2694}
2695
2696/** JIT-compile and run enough code to evaluate a Halide Tuple. */
2697 template<typename First, typename... Rest>
2698 HALIDE_NO_USER_CODE_INLINE void evaluate(JITUserContext *ctx, Tuple t, First first, Rest &&...rest) {
2699 Internal::check_types<First, Rest...>(t, 0);
2700
2701 Func f;
2702 f() = t;
2703 Realization r = f.realize(ctx);
2704 Internal::assign_results(r, 0, first, rest...);
2705}
2706
2707/** JIT-compile and run enough code to evaluate a Halide Tuple. */
2708 template<typename First, typename... Rest>
2709 HALIDE_NO_USER_CODE_INLINE void evaluate(Tuple t, First first, Rest &&...rest) {
2710 evaluate<First, Rest...>(nullptr, std::move(t), std::forward<First>(first), std::forward<Rest...>(rest...));
2711}
2712
2713 namespace Internal {
2714
2715 inline void schedule_scalar(Func f) {
2716 Target t = get_jit_target_from_environment();
2717 if (t.has_gpu_feature()) {
2718 f.gpu_single_thread();
2719 }
2720 if (t.has_feature(Target::HVX)) {
2721 f.hexagon();
2722 }
2723}
2724
2725} // namespace Internal
2726
2727/** JIT-Compile and run enough code to evaluate a Halide
2728 * expression. This can be thought of as a scalar version of
2729 * \ref Func::realize. Can use GPU if jit target from environment
2730 * specifies one.
2731 */
2732 template<typename T>
2733 HALIDE_NO_USER_CODE_INLINE T evaluate_may_gpu(const Expr &e) {
2734 user_assert(e.type() == type_of<T>())
2735 << "Can't evaluate expression "
2736 << e << " of type " << e.type()
2737 << " as a scalar of type " << type_of<T>() << "\n";
2738 Func f;
2739 f() = e;
2740 Internal::schedule_scalar(f);
2741 Buffer<T, 0> im = f.realize();
2742 return im();
2743}
2744
2745/** JIT-compile and run enough code to evaluate a Halide Tuple. Can
2746 * use GPU if jit target from environment specifies one. */
2747// @{
2748 template<typename First, typename... Rest>
2749 HALIDE_NO_USER_CODE_INLINE void evaluate_may_gpu(Tuple t, First first, Rest &&...rest) {
2750 Internal::check_types<First, Rest...>(t, 0);
2751
2752 Func f;
2753 f() = t;
2754 Internal::schedule_scalar(f);
2755 Realization r = f.realize();
2756 Internal::assign_results(r, 0, first, rest...);
2757}
2758// @}
2759
2760} // namespace Halide
2761
2762#endif
Argument.h
Defines a type used for expressing the type signature of a generated halide pipeline.
internal_assert
#define internal_assert(c)
Definition Error.h:232
user_assert
#define user_assert(c)
Definition Error.h:233
Expr.h
Base classes for Halide expressions (Halide::Expr) and statements (Halide::Internal::Stmt)
JITModule.h
Defines the struct representing lifetime and dependencies of a JIT compiled halide pipeline.
Module.h
Defines Module, an IR container that fully describes a Halide program.
Param.h
Classes for declaring scalar parameters to halide pipelines.
Pipeline.h
Defines the front-end class representing an entire Halide imaging pipeline.
RDom.h
Defines the front-end syntax for reduction domains and reduction variables.
Target.h
Defines the structure that describes a Halide target.
Tuple.h
Defines Tuple - the front-end handle on small arrays of expressions.
HALIDE_NO_USER_CODE_INLINE
#define HALIDE_NO_USER_CODE_INLINE
Definition Util.h:47
Var.h
Defines the Var - the front-end variable.
Halide::Buffer
A Halide::Buffer is a named shared reference to a Halide::Runtime::Buffer.
Definition Buffer.h:122
Halide::Callable
Definition Callable.h:82
Halide::EvictionKey
Helper class for identifying purpose of an Expr passed to memoize.
Definition Func.h:713
Halide::EvictionKey::key
Expr key
Definition Func.h:715
Halide::EvictionKey::EvictionKey
EvictionKey(const Expr &expr=Expr())
Definition Func.h:719
Halide::Func
A halide function.
Definition Func.h:728
Halide::Func::gpu
Func & gpu(const VarOrRVar &block_x, const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Tell Halide that the following dimensions correspond to GPU block indices and thread indices.
Halide::Func::gpu_blocks
Func & gpu_blocks(const VarOrRVar &block_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Tell Halide that the following dimensions correspond to GPU block indices.
Halide::Func::bound_extent
Func & bound_extent(const Var &var, Expr extent)
Bound the extent of a Func's realization, but not its min.
Halide::Func::print_loop_nest
void print_loop_nest()
Write out the loop nests specified by the schedule for this Function.
Halide::Func::unroll
Func & unroll(const VarOrRVar &var, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Split a dimension by the given factor, then unroll the inner dimension.
Halide::Func::is_extern
bool is_extern() const
Is this function an external stage? That is, was it defined using define_extern?
Halide::Func::operator()
FuncRef operator()(std::vector< Expr >) const
Either calls to the function, or the left-hand-side of an update definition (see RDom).
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &tx, const VarOrRVar &ty, const Expr &x_size, const Expr &y_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::Func
Func(const std::string &name)
Declare a new undefined function with the given name.
Halide::Func::compile_to_multitarget_object_files
void compile_to_multitarget_object_files(const std::string &filename_prefix, const std::vector< Argument > &args, const std::vector< Target > &targets, const std::vector< std::string > &suffixes)
Like compile_to_multitarget_static_library(), except that the object files are all output as object f...
Halide::Func::memoize
Func & memoize(const EvictionKey &eviction_key=EvictionKey())
Use the halide_memoization_cache_... interface to store a computed version of this function across in...
Halide::Func::partition
Func & partition(const VarOrRVar &var, Partition partition_policy)
Set the loop partition policy.
Halide::Func::trace_stores
Func & trace_stores()
Trace all stores to the buffer backing this Func by emitting calls to halide_trace.
Halide::Func::trace_loads
Func & trace_loads()
Trace all loads from this Func by emitting calls to halide_trace.
Halide::Func::never_partition_all
Func & never_partition_all()
Set the loop partition policy to Never for all Vars and RVar of the initial definition of the Func.
Halide::Func::prefetch
Func & prefetch(const Parameter &param, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Halide::Func::specialize_fail
void specialize_fail(const std::string &message)
Add a specialization to a Func that always terminates execution with a call to halide_error().
Halide::Func::vectorize
Func & vectorize(const VarOrRVar &var, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Split a dimension by the given factor, then vectorize the inner dimension.
Halide::Func::compute_at
Func & compute_at(const Func &f, const RVar &var)
Schedule a function to be computed within the iteration over some dimension of an update domain.
Halide::Func::operator()
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< Var, Args... >::value, FuncRef > operator()(Args &&...args) const
Definition Func.h:1259
Halide::Func::tile
Func & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &outers, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, TailStrategy tail=TailStrategy::Auto)
The generalized tile, with a single tail strategy to apply to all vars.
Halide::Func::compile_to_assembly
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_...
Halide::Func::function
Internal::Function function() const
Get a handle on the internal halide function that this Func represents.
Definition Func.h:2607
Halide::Func::has_update_definition
bool has_update_definition() const
Does this function have at least one update definition?
Halide::Func::compile_jit
void compile_jit(const Target &target=get_jit_target_from_environment())
Eagerly jit compile the function to machine code.
Halide::Func::compute_with
Func & compute_with(LoopLevel loop_level, const std::vector< std::pair< VarOrRVar, LoopAlignStrategy > > &align)
Halide::Func::Func
Func()
Declare a new undefined function with an automatically-generated unique name.
Halide::Func::store_in
Func & store_in(MemoryType memory_type)
Set the type of memory this Func should be stored in.
Halide::Func::type
const Type & type() const
Get the type(s) of the outputs of this Func.
Halide::Func::compile_to_bitcode
void compile_to_bitcode(const std::string &filename, const std::vector< Argument > &, const Target &target=get_target_from_environment())
Halide::Func::split_vars
std::vector< Var > split_vars() const
Get the Vars of the pure definition, with splits applied.
Halide::Func::infer_input_bounds
void infer_input_bounds(Pipeline::RealizationArg outputs, const Target &target=get_jit_target_from_environment())
Halide::Func::async
Func & async()
Produce this Func asynchronously in a separate thread.
Halide::Func::reorder
Func & reorder(const std::vector< VarOrRVar > &vars)
Reorder variables to have the given nesting order, from innermost out.
Halide::Func::gpu_blocks
Func & gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::set_estimate
Func & set_estimate(const Var &var, const Expr &min, const Expr &extent)
Statically declare the range over which the function will be evaluated in the general case.
Halide::Func::gpu_lanes
Func & gpu_lanes(const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
The given dimension corresponds to the lanes in a GPU warp.
Halide::Func::compile_to_lowered_stmt
void compile_to_lowered_stmt(const std::string &filename, const std::vector< Argument > &args, StmtOutputFormat fmt=Text, const Target &target=get_target_from_environment())
Write out an internal representation of lowered code.
Halide::Func::compile_to_c
void compile_to_c(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 C source code.
Halide::Func::update
Stage update(int idx=0)
Get a handle on an update step for the purposes of scheduling it.
Halide::Func::gpu_blocks
Func & gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::gpu_threads
Func & gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::Func
Func(const Type &required_type, int required_dims, const std::string &name)
Declare a new undefined function with the given name.
Halide::Func::defined
bool defined() const
Does this function have at least a pure definition.
Halide::Func::Func
Func(const std::vector< Type > &required_types, int required_dims, const std::string &name)
Declare a new undefined function with the given name.
Halide::Func::align_storage
Func & align_storage(const Var &dim, const Expr &alignment)
Pad the storage extent of a particular dimension of realizations of this function up to be a multiple...
Halide::Func::copy_to_host
Func copy_to_host()
Declare that this function should be implemented by a call to halide_buffer_copy with a NULL target d...
Halide::Func::infer_input_bounds
void infer_input_bounds(JITUserContext *context, Pipeline::RealizationArg outputs, const Target &target=get_jit_target_from_environment())
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &tx, const Expr &x_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::serial
Func & serial(const VarOrRVar &var)
Mark a dimension to be traversed serially.
Halide::Func::compile_to_header
void compile_to_header(const std::string &filename, const std::vector< Argument > &, const std::string &fn_name="", const Target &target=get_target_from_environment())
Emit a header file with the given filename for this function.
Halide::Func::align_bounds
Func & align_bounds(const Var &var, Expr modulus, Expr remainder=0)
Expand the region computed so that the min coordinates is congruent to 'remainder' modulo 'modulus',...
Halide::Func::reorder_storage
Func & reorder_storage(const Var &x, const Var &y)
Halide::Func::split
Func & split(const VarOrRVar &old, const VarOrRVar &outer, const VarOrRVar &inner, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Split a dimension into inner and outer subdimensions with the given names, where the inner dimension ...
Halide::Func::never_partition
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Func & > never_partition(const VarOrRVar &x, Args &&...args)
Set the loop partition policy to Never for some number of Vars and RVars.
Definition Func.h:1501
Halide::Func::Func
Func(const Expr &e)
Declare a new function with an automatically-generated unique name, and define it to return the given...
Halide::Func::tile
Func & tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &xo, const VarOrRVar &yo, const VarOrRVar &xi, const VarOrRVar &yi, const Expr &xfactor, const Expr &yfactor, TailStrategy tail=TailStrategy::Auto)
Split two dimensions at once by the given factors, and then reorder the resulting dimensions to be xi...
Halide::Func::dimensions
int dimensions() const
The dimensionality (number of arguments) of this function.
Halide::Func::gpu
Func & gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::compile_to_conceptual_stmt
void compile_to_conceptual_stmt(const std::string &filename, const std::vector< Argument > &args, StmtOutputFormat fmt=Text, const Target &target=get_target_from_environment())
Write out a conceptual representation of lowered code, before any parallel loop get factored out into...
Halide::Func::types
const std::vector< Type > & types() const
Halide::Func::operator()
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< Expr, Args... >::value, FuncRef > operator()(const Expr &x, Args &&...args) const
Definition Func.h:1276
Halide::Func::tile
Func & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &outers, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, const std::vector< TailStrategy > &tails)
A more general form of tile, which defines tiles of any dimensionality.
Halide::Func::always_partition
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Func & > always_partition(const VarOrRVar &x, Args &&...args)
Set the loop partition policy to Always for some number of Vars and RVars.
Definition Func.h:1518
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z, const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &bz, const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz, const Expr &x_size, const Expr &y_size, const Expr &z_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::update_args
const std::vector< Expr > & update_args(int idx=0) const
Get the left-hand-side of the update definition.
Halide::Func::realize
Realization realize(JITUserContext *context, std::vector< int32_t > sizes={}, const Target &target=Target())
Same as above, but takes a custom user-provided context to be passed to runtime functions.
Halide::Func::outputs
int outputs() const
Get the number of outputs of this Func.
Halide::Func::compute_root
Func & compute_root()
Compute all of this function once ahead of time.
Halide::Func::compute_with
Func & compute_with(LoopLevel loop_level, LoopAlignStrategy align=LoopAlignStrategy::Auto)
Halide::Func::trace_realizations
Func & trace_realizations()
Trace all realizations of this Func by emitting calls to halide_trace.
Halide::Func::jit_handlers
JITHandlers & jit_handlers()
Get a struct containing the currently set custom functions used by JIT.
Halide::Func::args
std::vector< Var > args() const
Get the pure arguments.
Halide::Func::update_values
Tuple update_values(int idx=0) const
Get the right-hand-side of an update definition for functions that returns multiple values.
Halide::Func::allow_race_conditions
Func & allow_race_conditions()
Specify that race conditions are permitted for this Func, which enables parallelizing over RVars even...
Halide::Func::compile_to_bitcode
void compile_to_bitcode(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 llvm bitcode, with the given filename (which should probably end ...
Halide::Func::num_update_definitions
int num_update_definitions() const
How many update definitions does this function have?
Halide::Func::tile
Func & tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &xi, const VarOrRVar &yi, const Expr &xfactor, const Expr &yfactor, TailStrategy tail=TailStrategy::Auto)
A shorter form of tile, which reuses the old variable names as the new outer dimensions.
Halide::Func::never_partition
Func & never_partition(const std::vector< VarOrRVar > &vars)
Set the loop partition policy to Never for a vector of Vars and RVars.
Halide::Func::specialize
Stage specialize(const Expr &condition)
Specialize a Func.
Halide::Func::compile_to_callable
Callable compile_to_callable(const std::vector< Argument > &args, const Target &target=get_jit_target_from_environment())
Eagerly jit compile the function to machine code and return a callable struct that behaves like a fun...
Halide::Func::ring_buffer
Func & ring_buffer(Expr extent)
Expands the storage of the function by an extra dimension to enable ring buffering.
Halide::Func::compute_with
Func & compute_with(const Stage &s, const VarOrRVar &var, LoopAlignStrategy align=LoopAlignStrategy::Auto)
Halide::Func::store_at
Func & store_at(LoopLevel loop_level)
Equivalent to the version of store_at that takes a Var, but schedules storage at a given LoopLevel.
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z, const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz, const Expr &x_size, const Expr &y_size, const Expr &z_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::Func
HALIDE_NO_USER_CODE_INLINE Func(Buffer< T, Dims > &im)
Construct a new Func to wrap a Buffer.
Definition Func.h:787
Halide::Func::define_extern
void define_extern(const std::string &function_name, const std::vector< ExternFuncArgument > &params, const std::vector< Type > &types, const std::vector< Var > &arguments, NameMangling mangling=NameMangling::Default, DeviceAPI device_api=DeviceAPI::Host)
Halide::Func::compute_with
Func & compute_with(const Stage &s, const VarOrRVar &var, const std::vector< std::pair< VarOrRVar, LoopAlignStrategy > > &align)
Schedule the iteration over the initial definition of this function to be fused with another stage 's...
Halide::Func::value
Expr value() const
The right-hand-side value of the pure definition of this function.
Halide::Func::align_extent
Func & align_extent(const Var &var, Expr modulus)
Expand the region computed so that the extent is a multiple of 'modulus'.
Halide::Func::infer_input_bounds
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...
Halide::Func::clone_in
Func clone_in(const std::vector< Func > &fs)
Halide::Func::compile_to_module
Module compile_to_module(const std::vector< Argument > &args, const std::string &fn_name="", const Target &target=get_target_from_environment())
Store an internal representation of lowered code as a self contained Module suitable for further comp...
Halide::Func::atomic
Func & atomic(bool override_associativity_test=false)
Issue atomic updates for this Func.
Halide::Func::define_extern
void define_extern(const std::string &function_name, const std::vector< ExternFuncArgument > &params, const std::vector< Type > &types, int dimensionality, NameMangling mangling=NameMangling::Default, DeviceAPI device_api=DeviceAPI::Host)
Definition Func.h:1192
Halide::Func::realize
void realize(Pipeline::RealizationArg outputs, const Target &target=Target())
Evaluate this function into an existing allocated buffer or buffers.
Halide::Func::unroll
Func & unroll(const VarOrRVar &var)
Mark a dimension to be completely unrolled.
Halide::Func::set_estimates
Func & set_estimates(const Region &estimates)
Set (min, extent) estimates for all dimensions in the Func at once; this is equivalent to calling set...
Halide::Func::in
Func in()
Create and return a global identity wrapper, which wraps all calls to this Func by any other Func.
Halide::Func::output_buffer
OutputImageParam output_buffer() const
Get a handle on the output buffer for this Func.
Halide::Func::update_value
Expr update_value(int idx=0) const
Get the right-hand-side of an update definition.
Halide::Func::compile_to
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.
Halide::Func::infer_arguments
std::vector< Argument > infer_arguments() const
Infer the arguments to the Func, sorted into a canonical order: all buffers (sorted alphabetically by...
Halide::Func::compile_to_llvm_assembly
void compile_to_llvm_assembly(const std::string &filename, const std::vector< Argument > &, const Target &target=get_target_from_environment())
Halide::Func::store_at
Func & store_at(const Func &f, const Var &var)
Allocate storage for this function within f's loop over var.
Halide::Func::add_custom_lowering_pass
void add_custom_lowering_pass(T *pass)
Add a custom pass to be used during lowering.
Definition Func.h:1087
Halide::Func::in
Func in(const std::vector< Func > &fs)
Create and return an identity wrapper shared by all the Funcs in 'fs'.
Halide::Func::fold_storage
Func & fold_storage(const Var &dim, const Expr &extent, bool fold_forward=true)
Store realizations of this function in a circular buffer of a given extent.
Halide::Func::hoist_storage_root
Func & hoist_storage_root()
Equivalent to Func::hoist_storage_root, but schedules storage outside the outermost loop.
Halide::Func::realize
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.
Halide::Func::realize
void realize(JITUserContext *context, Pipeline::RealizationArg outputs, const Target &target=Target())
Same as above, but takes a custom user-provided context to be passed to runtime functions.
Halide::Func::compute_at
Func & compute_at(LoopLevel loop_level)
Schedule a function to be computed within the iteration over a given LoopLevel.
Halide::Func::gpu_threads
Func & gpu_threads(const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Tell Halide that the following dimensions correspond to GPU thread indices.
Halide::Func::always_partition
Func & always_partition(const std::vector< VarOrRVar > &vars)
Set the loop partition policy to Always for a vector of Vars and RVars.
Halide::Func::define_extern
void define_extern(const std::string &function_name, const std::vector< ExternFuncArgument > &params, Type t, int dimensionality, NameMangling mangling=NameMangling::Default, DeviceAPI device_api=DeviceAPI::Host)
Add an extern definition for this Func.
Definition Func.h:1182
Halide::Func::compile_to_file
void compile_to_file(const std::string &filename_prefix, const std::vector< Argument > &args, const std::string &fn_name="", const Target &target=get_target_from_environment())
Compile to object file and header pair, with the given arguments.
Halide::Func::add_custom_lowering_pass
void add_custom_lowering_pass(Internal::IRMutator *pass, std::function< void()> deleter)
Add a custom pass to be used during lowering, with the function that will be called to delete it also...
Halide::Func::add_trace_tag
Func & add_trace_tag(const std::string &trace_tag)
Add a string of arbitrary text that will be passed thru to trace inspection code if the Func is reali...
Halide::Func::store_at
Func & store_at(const Func &f, const RVar &var)
Equivalent to the version of store_at that takes a Var, but schedules storage within the loop over a ...
Halide::Func::clear_custom_lowering_passes
void clear_custom_lowering_passes()
Remove all previously-set custom lowering passes.
Halide::Func::compile_to_llvm_assembly
void compile_to_llvm_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 llvm assembly, with the given filename (which should probably end...
Halide::Func::name
const std::string & name() const
The name of this function, either given during construction, or automatically generated.
Halide::Func::compile_to_multitarget_static_library
void compile_to_multitarget_static_library(const std::string &filename_prefix, const std::vector< Argument > &args, const std::vector< Target > &targets)
Compile to static-library file and header pair once for each target; each resulting function will be ...
Halide::Func::hexagon
Func & hexagon(const VarOrRVar &x=Var::outermost())
Schedule for execution on Hexagon.
Halide::Func::tile
Func & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, TailStrategy tail=TailStrategy::Auto)
Generalized tiling, reusing the previous names as the outer names.
Halide::Func::store_root
Func & store_root()
Equivalent to Func::store_at, but schedules storage outside the outermost loop.
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &tx, const VarOrRVar &ty, const Expr &x_size, const Expr &y_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::reorder
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Func & > reorder(const VarOrRVar &x, const VarOrRVar &y, Args &&...args)
Definition Func.h:1622
Halide::Func::prefetch
Func & prefetch(const Func &f, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Prefetch data written to or read from a Func or an ImageParam by a subsequent loop iteration,...
Halide::Func::compile_to_assembly
void compile_to_assembly(const std::string &filename, const std::vector< Argument > &, const Target &target=get_target_from_environment())
Halide::Func::rvars
std::vector< RVar > rvars(int idx=0) const
Get the RVars of the reduction domain for an update definition, if there is one.
Halide::Func::clone_in
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...
Halide::Func::custom_lowering_passes
const std::vector< CustomLoweringPass > & custom_lowering_passes()
Get the custom lowering passes.
Halide::Func::hoist_storage
Func & hoist_storage(const Func &f, const Var &var)
Hoist storage for this function within f's loop over var.
Halide::Func::compute_inline
Func & compute_inline()
Aggressively inline all uses of this function.
Halide::Func::Func
Func(Internal::Function f)
Construct a new Func to wrap an existing, already-defined Function object.
Halide::Func::compile_to_object
void compile_to_object(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 an object file, with the given filename (which should probably en...
Halide::Func::bound_storage
Func & bound_storage(const Var &dim, const Expr &bound)
Bound the extent of a Func's storage, but not extent of its compute.
Halide::Func::rename
Func & rename(const VarOrRVar &old_name, const VarOrRVar &new_name)
Rename a dimension.
Halide::Func::values
Tuple values() const
The values returned by this function.
Halide::Func::extern_function_name
const std::string & extern_function_name() const
Get the name of the extern function called for an extern definition.
Halide::Func::copy_to_device
Func copy_to_device(DeviceAPI d=DeviceAPI::Default_GPU)
Declare that this function should be implemented by a call to halide_buffer_copy with the given targe...
Halide::Func::parallel
Func & parallel(const VarOrRVar &var)
Mark a dimension to be traversed in parallel.
Halide::Func::gpu_threads
Func & gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::compile_to_object
void compile_to_object(const std::string &filename, const std::vector< Argument > &, const Target &target=get_target_from_environment())
Halide::Func::hoist_storage
Func & hoist_storage(LoopLevel loop_level)
Equivalent to the version of hoist_storage that takes a Var, but schedules storage at a given LoopLev...
Halide::Func::reorder_storage
Func & reorder_storage(const std::vector< Var > &dims)
Specify how the storage for the function is laid out.
Halide::Func::gpu
Func & gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Func::no_profiling
Func & no_profiling()
Marks this function as a function that should not be profiled when using the target feature Profile o...
Halide::Func::define_extern
void define_extern(const std::string &function_name, const std::vector< ExternFuncArgument > &params, Type t, const std::vector< Var > &arguments, NameMangling mangling=NameMangling::Default, DeviceAPI device_api=DeviceAPI::Host)
Definition Func.h:1202
Halide::Func::reorder_storage
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< Var, Args... >::value, Func & > reorder_storage(const Var &x, const Var &y, Args &&...args)
Definition Func.h:2104
Halide::Func::infer_input_bounds
void infer_input_bounds(JITUserContext *context, const std::vector< int32_t > &sizes, const Target &target=get_jit_target_from_environment())
Versions of infer_input_bounds that take a custom user context to pass to runtime functions.
Halide::Func::vectorize
Func & vectorize(const VarOrRVar &var)
Mark a dimension to be computed all-at-once as a single vector.
Halide::Func::debug_to_file
void debug_to_file(const std::string &filename)
When this function is compiled, include code that dumps its values to a file after it is realized,...
Halide::Func::parallel
Func & parallel(const VarOrRVar &var, const Expr &task_size, TailStrategy tail=TailStrategy::Auto)
Split a dimension by the given task_size, and the parallelize the outer dimension.
Halide::Func::fuse
Func & fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRVar &fused)
Join two dimensions into a single fused dimension.
Halide::Func::in
Func in(const Func &f)
Creates and returns a new identity Func that wraps this Func.
Halide::Func::bound
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...
Halide::Func::output_buffers
std::vector< OutputImageParam > output_buffers() const
Halide::Func::prefetch
Func & prefetch(const T &image, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Definition Func.h:2078
Halide::Func::compile_to_static_library
void compile_to_static_library(const std::string &filename_prefix, const std::vector< Argument > &args, const std::string &fn_name="", const Target &target=get_target_from_environment())
Compile to static-library file and header pair, with the given arguments.
Halide::Func::compute_at
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...
Halide::Func::hoist_storage
Func & hoist_storage(const Func &f, const RVar &var)
Equivalent to the version of hoist_storage that takes a Var, but schedules storage within the loop ov...
Halide::Func::operator()
FuncRef operator()(std::vector< Var >) const
Construct either the left-hand-side of a definition, or a call to a functions that happens to only co...
Halide::Func::always_partition_all
Func & always_partition_all()
Set the loop partition policy to Always for all Vars and RVar of the initial definition of the Func.
Halide::Func::get_schedule
const Internal::StageSchedule & get_schedule() const
Return the current StageSchedule associated with this initial Stage of this Func.
Definition Func.h:2639
Halide::Func::gpu_single_thread
Func & gpu_single_thread(DeviceAPI device_api=DeviceAPI::Default_GPU)
Tell Halide to run this stage using a single gpu thread and block.
Halide::Func::gpu_tile
Func & gpu_tile(const VarOrRVar &x, const VarOrRVar &bx, const VarOrRVar &tx, const Expr &x_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Short-hand for tiling a domain and mapping the tile indices to GPU block indices and the coordinates ...
Halide::FuncRef
A fragment of front-end syntax of the form f(x, y, z), where x, y, z are Vars or Exprs.
Definition Func.h:502
Halide::FuncRef::operator*=
Stage operator*=(const FuncRef &)
Halide::FuncRef::operator[]
FuncTupleElementRef operator[](int) const
When a FuncRef refers to a function that provides multiple outputs, you can access each output as an ...
Halide::FuncRef::operator/=
Stage operator/=(const Expr &)
Define a stage that divides this Func by the given expression.
Halide::FuncRef::operator-=
Stage operator-=(const FuncRef &)
Halide::FuncRef::size
size_t size() const
How many outputs does the function this refers to produce.
Halide::FuncRef::function
Internal::Function function() const
What function is this calling?
Definition Func.h:616
Halide::FuncRef::operator-=
Stage operator-=(const Tuple &)
Halide::FuncRef::operator+=
Stage operator+=(const FuncRef &)
Halide::FuncRef::operator=
Stage operator=(const Expr &)
Use this as the left-hand-side of a definition or an update definition (see RDom).
Halide::FuncRef::type
const Type & type() const
Get the type(s) of the outputs of the Func to which this FuncRef refers.
Halide::FuncRef::operator=
Stage operator=(const FuncRef &)
Halide::FuncRef::FuncRef
FuncRef(Internal::Function, const std::vector< Var > &, int placeholder_pos=-1, int count=0)
Halide::FuncRef::operator+=
Stage operator+=(const Tuple &)
Halide::FuncRef::operator-=
Stage operator-=(const Expr &)
Define a stage that adds the negative of the given expression to this Func.
Halide::FuncRef::operator*=
Stage operator*=(const Expr &)
Define a stage that multiplies this Func by the given expression.
Halide::FuncRef::operator+=
Stage operator+=(const Expr &)
Define a stage that adds the given expression to this Func.
Halide::FuncRef::types
const std::vector< Type > & types() const
Halide::FuncRef::equivalent_to
bool equivalent_to(const FuncRef &other) const
Is this FuncRef syntactically equivalent to another one?
Halide::FuncRef::FuncRef
FuncRef(const Internal::Function &, const std::vector< Expr > &, int placeholder_pos=-1, int count=0)
Halide::FuncRef::operator/=
Stage operator/=(const FuncRef &)
Halide::FuncRef::operator*=
Stage operator*=(const Tuple &)
Halide::FuncRef::operator/=
Stage operator/=(const Tuple &)
Halide::FuncRef::operator=
Stage operator=(const Tuple &)
Use this as the left-hand-side of a definition or an update definition for a Func with multiple outpu...
Halide::FuncTupleElementRef
A fragment of front-end syntax of the form f(x, y, z)[index], where x, y, z are Vars or Exprs.
Definition Func.h:638
Halide::FuncTupleElementRef::index
int index() const
Return index to the function outputs.
Definition Func.h:702
Halide::FuncTupleElementRef::operator+=
Stage operator+=(const Expr &e)
Define a stage that adds the given expression to Tuple component 'idx' of this Func.
Halide::FuncTupleElementRef::operator*=
Stage operator*=(const Expr &e)
Define a stage that multiplies Tuple component 'idx' of this Func by the given expression.
Halide::FuncTupleElementRef::operator/=
Stage operator/=(const Expr &e)
Define a stage that divides Tuple component 'idx' of this Func by the given expression.
Halide::FuncTupleElementRef::operator=
Stage operator=(const Expr &e)
Use this as the left-hand-side of an update definition of Tuple component 'idx' of a Func (see RDom).
Halide::FuncTupleElementRef::operator=
Stage operator=(const FuncRef &e)
Halide::FuncTupleElementRef::function
Internal::Function function() const
What function is this calling?
Definition Func.h:697
Halide::FuncTupleElementRef::operator-=
Stage operator-=(const Expr &e)
Define a stage that adds the negative of the given expression to Tuple component 'idx' of this Func.
Halide::FuncTupleElementRef::FuncTupleElementRef
FuncTupleElementRef(const FuncRef &ref, const std::vector< Expr > &args, int idx)
Halide::ImageParam
An Image parameter to a halide pipeline.
Definition ImageParam.h:23
Halide::Internal::Definition
A Function definition which can either represent a init or an update definition.
Definition Definition.h:38
Halide::Internal::Definition::args
const std::vector< Expr > & args() const
Get the default (no-specialization) arguments (left-hand-side) of the definition.
Halide::Internal::Definition::schedule
const StageSchedule & schedule() const
Get the default (no-specialization) stage-specific schedule associated with this definition.
Halide::Internal::Definition::defined
bool defined() const
Definition objects are nullable.
Halide::Internal::FuncSchedule::storage_dims
const std::vector< StorageDim > & storage_dims() const
The list and order of dimensions used to store this function.
Halide::Internal::Function
A reference-counted handle to Halide's internal representation of a function.
Definition Function.h:39
Halide::Internal::Function::schedule
FuncSchedule & schedule()
Get a handle to the function-specific schedule for the purpose of modifying it.
Halide::Internal::Function::args
const std::vector< std::string > & args() const
Get the pure arguments.
Halide::Internal::IRMutator
A base class for passes over the IR which modify it (e.g.
Definition IRMutator.h:28
Halide::Internal::StageSchedule
A schedule for a single stage of a Halide pipeline.
Definition Schedule.h:680
Halide::LoopLevel
A reference to a site in a Halide statement at the top of the body of a particular for loop.
Definition Schedule.h:203
Halide::Module
A halide module.
Definition Module.h:142
Halide::OutputImageParam
A handle on the output buffer of a pipeline.
Definition OutputImageParam.h:19
Halide::Parameter
A reference-counted handle to a parameter to a halide pipeline.
Definition Parameter.h:40
Halide::Pipeline
A class representing a Halide pipeline.
Definition Pipeline.h:107
Halide::RDom
A multi-dimensional domain over which to iterate.
Definition RDom.h:193
Halide::RVar
A reduction variable represents a single dimension of a reduction domain (RDom).
Definition RDom.h:29
Halide::RVar::name
const std::string & name() const
The name of this reduction variable.
Halide::Realization
A Realization is a vector of references to existing Buffer objects.
Definition Realization.h:19
Halide::Stage
A single definition of a Func.
Definition Func.h:70
Halide::Stage::prefetch
Stage & prefetch(const Func &f, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Halide::Stage::name
std::string name() const
Return the name of this stage, e.g.
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &tx, const VarOrRVar &ty, const Expr &x_size, const Expr &y_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::tile
Stage & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &tx, const Expr &x_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::gpu_threads
Stage & gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::compute_with
Stage & compute_with(const Stage &s, const VarOrRVar &var, const std::vector< std::pair< VarOrRVar, LoopAlignStrategy > > &align)
Halide::Stage::rfactor
Func rfactor(const RVar &r, const Var &v)
Halide::Stage::gpu
Stage & gpu(const VarOrRVar &block_x, const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::vectorize
Stage & vectorize(const VarOrRVar &var)
Halide::Stage::reorder
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Stage & > reorder(const VarOrRVar &x, const VarOrRVar &y, Args &&...args)
Definition Func.h:386
Halide::Stage::never_partition
Stage & never_partition(const std::vector< VarOrRVar > &vars)
Halide::Stage::gpu_lanes
Stage & gpu_lanes(const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::unroll
Stage & unroll(const VarOrRVar &var)
Halide::Stage::compute_with
Stage & compute_with(LoopLevel loop_level, const std::vector< std::pair< VarOrRVar, LoopAlignStrategy > > &align)
Schedule the iteration over this stage to be fused with another stage 's' from outermost loop to a gi...
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z, const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz, const Expr &x_size, const Expr &y_size, const Expr &z_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &bx, const VarOrRVar &tx, const Expr &x_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::split_vars
std::vector< VarOrRVar > split_vars() const
Get the Vars and RVars of this definition, from innermost out, with splits applied.
Halide::Stage::allow_race_conditions
Stage & allow_race_conditions()
Halide::Stage::tile
Stage & tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &xi, const VarOrRVar &yi, const Expr &xfactor, const Expr &yfactor, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::parallel
Stage & parallel(const VarOrRVar &var, const Expr &task_size, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::rename
Stage & rename(const VarOrRVar &old_name, const VarOrRVar &new_name)
Halide::Stage::tile
Stage & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &outers, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::gpu_single_thread
Stage & gpu_single_thread(DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::partition
Stage & partition(const VarOrRVar &var, Partition partition_policy)
Halide::Stage::unroll
Stage & unroll(const VarOrRVar &var, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::specialize
Stage specialize(const Expr &condition)
Halide::Stage::prefetch
Stage & prefetch(const T &image, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Definition Func.h:471
Halide::Stage::gpu_threads
Stage & gpu_threads(const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::prefetch
Stage & prefetch(const Parameter &param, const VarOrRVar &at, const VarOrRVar &from, Expr offset=1, PrefetchBoundStrategy strategy=PrefetchBoundStrategy::GuardWithIf)
Halide::Stage::reorder
Stage & reorder(const std::vector< VarOrRVar > &vars)
Halide::Stage::gpu
Stage & gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, const VarOrRVar &thread_x, const VarOrRVar &thread_y, const VarOrRVar &thread_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::Stage
Stage(Internal::Function f, Internal::Definition d, size_t stage_index)
Definition Func.h:96
Halide::Stage::always_partition
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Stage & > always_partition(const VarOrRVar &x, Args &&...args)
Definition Func.h:400
Halide::Stage::gpu_blocks
Stage & gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &block_z, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::tile
Stage & tile(const std::vector< VarOrRVar > &previous, const std::vector< VarOrRVar > &outers, const std::vector< VarOrRVar > &inners, const std::vector< Expr > &factors, const std::vector< TailStrategy > &tails)
Halide::Stage::compute_with
Stage & compute_with(LoopLevel loop_level, LoopAlignStrategy align=LoopAlignStrategy::Auto)
Halide::Stage::parallel
Stage & parallel(const VarOrRVar &var)
Halide::Stage::get_schedule
const Internal::StageSchedule & get_schedule() const
Return the current StageSchedule associated with this Stage.
Definition Func.h:109
Halide::Stage::serial
Stage & serial(const VarOrRVar &var)
Halide::Stage::gpu_blocks
Stage & gpu_blocks(const VarOrRVar &block_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::fuse
Stage & fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRVar &fused)
Halide::Stage::vectorize
Stage & vectorize(const VarOrRVar &var, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::gpu
Stage & gpu(const VarOrRVar &block_x, const VarOrRVar &block_y, const VarOrRVar &thread_x, const VarOrRVar &thread_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::compute_with
Stage & compute_with(const Stage &s, const VarOrRVar &var, LoopAlignStrategy align=LoopAlignStrategy::Auto)
Halide::Stage::specialize_fail
void specialize_fail(const std::string &message)
Halide::Stage::tile
Stage & tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &xo, const VarOrRVar &yo, const VarOrRVar &xi, const VarOrRVar &yi, const Expr &xfactor, const Expr &yfactor, TailStrategy tail=TailStrategy::Auto)
Halide::Stage::gpu_blocks
Stage & gpu_blocks(const VarOrRVar &block_x, const VarOrRVar &block_y, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::hexagon
Stage & hexagon(const VarOrRVar &x=Var::outermost())
Halide::Stage::split
Stage & split(const VarOrRVar &old, const VarOrRVar &outer, const VarOrRVar &inner, const Expr &factor, TailStrategy tail=TailStrategy::Auto)
Scheduling calls that control how the domain of this stage is traversed.
Halide::Stage::always_partition_all
Stage & always_partition_all()
Halide::Stage::never_partition_all
Stage & never_partition_all()
Halide::Stage::atomic
Stage & atomic(bool override_associativity_test=false)
Halide::Stage::gpu_threads
Stage & gpu_threads(const VarOrRVar &thread_x, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::rfactor
Func rfactor(const std::vector< std::pair< RVar, Var > > &preserved)
Calling rfactor() on an associative update definition a Func will split the update into an intermedia...
Halide::Stage::dump_argument_list
std::string dump_argument_list() const
Return a string describing the current var list taking into account all the splits,...
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &tx, const VarOrRVar &ty, const Expr &x_size, const Expr &y_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::gpu_tile
Stage & gpu_tile(const VarOrRVar &x, const VarOrRVar &y, const VarOrRVar &z, const VarOrRVar &bx, const VarOrRVar &by, const VarOrRVar &bz, const VarOrRVar &tx, const VarOrRVar &ty, const VarOrRVar &tz, const Expr &x_size, const Expr &y_size, const Expr &z_size, TailStrategy tail=TailStrategy::Auto, DeviceAPI device_api=DeviceAPI::Default_GPU)
Halide::Stage::unscheduled
void unscheduled()
Assert that this stage has intentionally been given no schedule, and suppress the warning about unsch...
Halide::Stage::always_partition
Stage & always_partition(const std::vector< VarOrRVar > &vars)
Halide::Stage::never_partition
HALIDE_NO_USER_CODE_INLINE std::enable_if_t< Internal::all_are_convertible< VarOrRVar, Args... >::value, Stage & > never_partition(const VarOrRVar &x, Args &&...args)
Definition Func.h:393
Halide::Tuple
Create a small array of Exprs for defining and calling functions with multiple outputs.
Definition Tuple.h:18
Halide::Var
A Halide variable, to be used when defining functions.
Definition Var.h:19
Halide::Var::name
const std::string & name() const
Get the name of a Var.
Halide::Var::outermost
static Var outermost()
A Var that represents the location outside the outermost loop.
Definition Var.h:162
Halide::Internal::schedule_scalar
void schedule_scalar(Func f)
Definition Func.h:2715
Halide::Internal::make_argument_list
std::vector< Var > make_argument_list(int dimensionality)
Make a list of unique arguments for definitions with unnamed arguments.
Halide::Internal::assign_results
void assign_results(Realization &r, int idx, Last last)
Definition Func.h:2662
Halide::Internal::check_types
void check_types(const Tuple &t, int idx)
Definition Func.h:2647
Halide::Internal::ForType
ForType
An enum describing a type of loop traversal.
Definition Expr.h:406
Halide
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Definition AbstractGenerator.h:19
Halide::LinkageType::Internal
@ Internal
Not visible externally, similar to 'static' linkage in C.
Halide::PrefetchBoundStrategy
PrefetchBoundStrategy
Different ways to handle accesses outside the original extents in a prefetch.
Definition PrefetchDirective.h:16
Halide::PrefetchBoundStrategy::GuardWithIf
@ GuardWithIf
Guard the prefetch with if-guards that ignores the prefetch if any of the prefetched region ever goes...
Halide::evaluate_may_gpu
HALIDE_NO_USER_CODE_INLINE T evaluate_may_gpu(const Expr &e)
JIT-Compile and run enough code to evaluate a Halide expression.
Definition Func.h:2733
Halide::TailStrategy
TailStrategy
Different ways to handle a tail case in a split when the factor does not provably divide the extent.
Definition Schedule.h:33
Halide::TailStrategy::Auto
@ Auto
For pure definitions use ShiftInwards.
Halide::LoopAlignStrategy
LoopAlignStrategy
Different ways to handle the case when the start/end of the loops of stages computed with (fused) are...
Definition Schedule.h:137
Halide::LoopAlignStrategy::Auto
@ Auto
By default, LoopAlignStrategy is set to NoAlign.
Halide::NameMangling
NameMangling
An enum to specify calling convention for extern stages.
Definition Function.h:26
Halide::NameMangling::Default
@ Default
Match whatever is specified in the Target.
Halide::get_jit_target_from_environment
Target get_jit_target_from_environment()
Return the target that Halide will use for jit-compilation.
Halide::DeviceAPI
DeviceAPI
An enum describing a type of device API.
Definition DeviceAPI.h:15
Halide::DeviceAPI::Host
@ Host
Used to denote for loops that run on the same device as the containing code.
Halide::DeviceAPI::Default_GPU
@ Default_GPU
Halide::get_target_from_environment
Target get_target_from_environment()
Return the target that Halide will use.
Halide::cast
Internal::ConstantInterval cast(Type t, const Internal::ConstantInterval &a)
Cast operators for ConstantIntervals.
Halide::StmtOutputFormat
StmtOutputFormat
Used to determine if the output printed to file should be as a normal string or as an HTML file which...
Definition Pipeline.h:72
Halide::Text
@ Text
Definition Pipeline.h:73
Halide::ScheduleHandle
Stage ScheduleHandle
Definition Func.h:493
Halide::Region
std::vector< Range > Region
A multi-dimensional box.
Definition Expr.h:350
Halide::MemoryType
MemoryType
An enum describing different address spaces to be used with Func::store_in.
Definition Expr.h:353
Halide::Partition
Partition
Different ways to handle loops with a potentially optimizable boundary conditions.
Definition LoopPartitioningDirective.h:16
Halide::evaluate
HALIDE_NO_USER_CODE_INLINE T evaluate(JITUserContext *ctx, const Expr &e)
JIT-Compile and run enough code to evaluate a Halide expression.
Definition Func.h:2679
Halide::Expr
A fragment of Halide syntax.
Definition Expr.h:258
Halide::Expr::type
HALIDE_ALWAYS_INLINE Type type() const
Get the type of this expression node.
Definition Expr.h:327
Halide::ExternFuncArgument
An argument to an extern-defined Func.
Definition ExternFuncArgument.h:17
Halide::ImplicitVar
Definition Var.h:168
Halide::Internal::AssociativeOp
Represent the equivalent associative op of an update definition.
Definition Associativity.h:61
Halide::Internal::all_are_convertible
Definition Util.h:263
Halide::JITHandlers
A set of custom overrides of runtime functions.
Definition JITModule.h:35
Halide::JITUserContext
A context to be passed to Pipeline::realize.
Definition JITModule.h:136
Halide::Pipeline::RealizationArg
Definition Pipeline.h:109
Halide::Target
A struct representing a target machine and os to generate code for.
Definition Target.h:19
Halide::Target::has_gpu_feature
bool has_gpu_feature() const
Is a fully feature GPU compute runtime enabled? I.e.
Halide::Target::has_feature
bool has_feature(Feature f) const
Halide::Target::HVX
@ HVX
Definition Target.h:124
Halide::Type
Types in the halide type system.
Definition Type.h:281
Halide::VarOrRVar
A class that can represent Vars or RVars.
Definition Func.h:29
Halide::VarOrRVar::is_rvar
bool is_rvar
Definition Func.h:57
Halide::VarOrRVar::VarOrRVar
VarOrRVar(const Var &v)
Definition Func.h:33
Halide::VarOrRVar::VarOrRVar
VarOrRVar(const RVar &r)
Definition Func.h:36
Halide::VarOrRVar::var
Var var
Definition Func.h:55
Halide::VarOrRVar::VarOrRVar
VarOrRVar(const std::string &n, bool r)
Definition Func.h:30
Halide::VarOrRVar::VarOrRVar
VarOrRVar(const ImplicitVar< N > &u)
Definition Func.h:43
Halide::VarOrRVar::name
const std::string & name() const
Definition Func.h:47
Halide::VarOrRVar::VarOrRVar
VarOrRVar(const RDom &r)
Definition Func.h:39
Halide::VarOrRVar::rvar
RVar rvar
Definition Func.h:56