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