1 #ifndef HALIDE_CODEGEN_C_H
2 #define HALIDE_CODEGEN_C_H
3 
4 /** \file
5  *
6  * Defines an IRPrinter that emits C++ code equivalent to a halide stmt
7  */
8 
9 #include "IRPrinter.h"
10 #include "Module.h"
11 #include "Scope.h"
12 
13 namespace Halide {
14 
15 struct Argument;
16 
17 namespace Internal {
18 
19 /** This class emits C++ code equivalent to a halide Stmt. It's
20  * mostly the same as an IRPrinter, but it's wrapped in a function
21  * definition, and some things are handled differently to be valid
22  * C++.
23  */
24 class CodeGen_C : public IRPrinter {
25 public:
26     enum OutputKind {
27         CHeader,
28         CPlusPlusHeader,
29         CImplementation,
30         CPlusPlusImplementation,
31         CExternDecl,
32         CPlusPlusExternDecl,
33     };
34 
35     /** Initialize a C code generator pointing at a particular output
36      * stream (e.g. a file, or std::cout) */
37     CodeGen_C(std::ostream &dest,
38               Target target,
39               OutputKind output_kind = CImplementation,
40               const std::string &include_guard = "");
41     ~CodeGen_C() override;
42 
43     /** Emit the declarations contained in the module as C code. */
44     void compile(const Module &module);
45 
46     /** The target we're generating code for */
get_target()47     const Target &get_target() const {
48         return target;
49     }
50 
51     static void test();
52 
53     /**  Add common macros to be shared across all backends */
54     void add_common_macros(std::ostream &dest);
55 
56 protected:
57     /** Emit a declaration. */
58     // @{
59     virtual void compile(const LoweredFunc &func);
60     virtual void compile(const Buffer<> &buffer);
61     // @}
62 
63     /** An ID for the most recently generated ssa variable */
64     std::string id;
65 
66     /** The target being generated for. */
67     Target target;
68 
69     /** Controls whether this instance is generating declarations or
70      * definitions and whether the interface us extern "C" or C++. */
71     OutputKind output_kind;
72 
73     /** A cache of generated values in scope */
74     std::map<std::string, std::string> cache;
75 
76     /** Emit an expression as an assignment, then return the id of the
77      * resulting var */
78     std::string print_expr(const Expr &);
79 
80     /** Like print_expr, but cast the Expr to the given Type */
81     std::string print_cast_expr(const Type &, const Expr &);
82 
83     /** Emit a statement */
84     void print_stmt(const Stmt &);
85 
86     void create_assertion(const std::string &id_cond, const Expr &message);
87     void create_assertion(const Expr &cond, const Expr &message);
88 
89     enum AppendSpaceIfNeeded {
90         DoNotAppendSpace,
91         AppendSpace,
92     };
93 
94     /** Emit the C name for a halide type. If space_option is AppendSpace,
95      *  and there should be a space between the type and the next token,
96      *  one is appended. (This allows both "int foo" and "Foo *foo" to be
97      *  formatted correctly. Otherwise the latter is "Foo * foo".)
98      */
99     virtual std::string print_type(Type, AppendSpaceIfNeeded space_option = DoNotAppendSpace);
100 
101     /** Emit a statement to reinterpret an expression as another type */
102     virtual std::string print_reinterpret(Type, const Expr &);
103 
104     /** Emit a version of a string that is a valid identifier in C (. is replaced with _) */
105     virtual std::string print_name(const std::string &);
106 
107     /** Add typedefs for vector types. Not needed for OpenCL, might
108      * use different syntax for other C-like languages. */
109     virtual void add_vector_typedefs(const std::set<Type> &vector_types);
110 
111     /** Bottleneck to allow customization of calls to generic Extern/PureExtern calls.  */
112     virtual std::string print_extern_call(const Call *op);
113 
114     /** Convert a vector Expr into a series of scalar Exprs, then reassemble into vector of original type.  */
115     std::string print_scalarized_expr(const Expr &e);
116 
117     /** Emit an SSA-style assignment, and set id to the freshly generated name. Return id. */
118     virtual std::string print_assignment(Type t, const std::string &rhs);
119 
120     /** Emit free for the heap allocation. **/
121     void print_heap_free(const std::string &alloc_name);
122 
123     /** Return true if only generating an interface, which may be extern "C" or C++ */
is_header()124     bool is_header() {
125         return output_kind == CHeader ||
126                output_kind == CPlusPlusHeader;
127     }
128 
129     /** Return true if only generating an interface, which may be extern "C" or C++ */
is_extern_decl()130     bool is_extern_decl() {
131         return output_kind == CExternDecl ||
132                output_kind == CPlusPlusExternDecl;
133     }
134 
135     /** Return true if only generating an interface, which may be extern "C" or C++ */
is_header_or_extern_decl()136     bool is_header_or_extern_decl() {
137         return is_header() || is_extern_decl();
138     }
139 
140     /** Return true if generating C++ linkage. */
is_c_plus_plus_interface()141     bool is_c_plus_plus_interface() {
142         return output_kind == CPlusPlusHeader ||
143                output_kind == CPlusPlusImplementation ||
144                output_kind == CPlusPlusExternDecl;
145     }
146 
147     /** Open a new C scope (i.e. throw in a brace, increase the indent) */
148     void open_scope();
149 
150     /** Close a C scope (i.e. throw in an end brace, decrease the indent) */
151     void close_scope(const std::string &comment);
152 
153     struct Allocation {
154         Type type;
155     };
156 
157     /** Track the types of allocations to avoid unnecessary casts. */
158     Scope<Allocation> allocations;
159 
160     /** Track which allocations actually went on the heap. */
161     Scope<> heap_allocations;
162 
163     /** True if there is a void * __user_context parameter in the arguments. */
164     bool have_user_context;
165 
166     /** Track current calling convention scope. */
167     bool extern_c_open;
168 
169     /** True if at least one gpu-based for loop is used. */
170     bool uses_gpu_for_loops;
171 
172     /** Track which handle types have been forward-declared already. */
173     std::set<const halide_handle_cplusplus_type *> forward_declared;
174 
175     /** If the Type is a handle type, emit a forward-declaration for it
176      * if we haven't already. */
177     void forward_declare_type_if_needed(const Type &t);
178 
179     void set_name_mangling_mode(NameMangling mode);
180 
181     using IRPrinter::visit;
182 
183     void visit(const Variable *) override;
184     void visit(const IntImm *) override;
185     void visit(const UIntImm *) override;
186     void visit(const StringImm *) override;
187     void visit(const FloatImm *) override;
188     void visit(const Cast *) override;
189     void visit(const Add *) override;
190     void visit(const Sub *) override;
191     void visit(const Mul *) override;
192     void visit(const Div *) override;
193     void visit(const Mod *) override;
194     void visit(const Max *) override;
195     void visit(const Min *) override;
196     void visit(const EQ *) override;
197     void visit(const NE *) override;
198     void visit(const LT *) override;
199     void visit(const LE *) override;
200     void visit(const GT *) override;
201     void visit(const GE *) override;
202     void visit(const And *) override;
203     void visit(const Or *) override;
204     void visit(const Not *) override;
205     void visit(const Call *) override;
206     void visit(const Select *) override;
207     void visit(const Load *) override;
208     void visit(const Store *) override;
209     void visit(const Let *) override;
210     void visit(const LetStmt *) override;
211     void visit(const AssertStmt *) override;
212     void visit(const ProducerConsumer *) override;
213     void visit(const For *) override;
214     void visit(const Ramp *) override;
215     void visit(const Broadcast *) override;
216     void visit(const Provide *) override;
217     void visit(const Allocate *) override;
218     void visit(const Free *) override;
219     void visit(const Realize *) override;
220     void visit(const IfThenElse *) override;
221     void visit(const Evaluate *) override;
222     void visit(const Shuffle *) override;
223     void visit(const Prefetch *) override;
224     void visit(const Fork *) override;
225     void visit(const Acquire *) override;
226     void visit(const Atomic *) override;
227 
228     void visit_binop(Type t, const Expr &a, const Expr &b, const char *op);
229 
230     template<typename T>
with_sep(const std::vector<T> & v,const std::string & sep)231     static std::string with_sep(const std::vector<T> &v, const std::string &sep) {
232         std::ostringstream o;
233         for (size_t i = 0; i < v.size(); ++i) {
234             if (i > 0) {
235                 o << sep;
236             }
237             o << v[i];
238         }
239         return o.str();
240     }
241 
242     template<typename T>
with_commas(const std::vector<T> & v)243     static std::string with_commas(const std::vector<T> &v) {
244         return with_sep<T>(v, ", ");
245     }
246 
247     /** Are we inside an atomic node that uses mutex locks?
248         This is used for detecting deadlocks from nested atomics. */
249     bool inside_atomic_mutex_node;
250 
251     /** Emit atomic store instructions? */
252     bool emit_atomic_stores;
253 };
254 
255 }  // namespace Internal
256 }  // namespace Halide
257 
258 #endif
259