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