+/* builder */
+#define IR_HT_SIZE 1024
+#define IR_MAX_VINSTR_TEMPS 1
+
+struct ir_builder {
+ ir_builder(const std::string& modulename);
+ ~ir_builder();
+
+ ir_function *createFunction(const std::string &name, qc_type outtype);
+ ir_value *createGlobal(const std::string &name, qc_type vtype);
+ ir_value *createField(const std::string &name, qc_type vtype);
+ ir_value *get_va_count();
+ bool generate(const char *filename);
+ void dump(int (*oprintf)(const char*, ...)) const;
+
+ ir_value *generateExtparamProto();
+ void generateExtparam();
+
+ ir_value *literalFloat(float value, bool add_to_list);
+
+ std::string m_name;
+ std::vector<std::unique_ptr<ir_function>> m_functions;
+ std::vector<std::unique_ptr<ir_value>> m_globals;
+ std::vector<std::unique_ptr<ir_value>> m_fields;
+ // for reusing them in vector-splits, TODO: sort this or use a radix-tree
+ std::vector<ir_value*> m_const_floats;
+
+ ht m_htfunctions;
+ ht m_htglobals;
+ ht m_htfields;
+
+ // extparams' ir_values reference the ones from extparam_protos
+ std::vector<std::unique_ptr<ir_value>> m_extparam_protos;
+ std::vector<ir_value*> m_extparams;
+
+ // the highest func->allocated_locals
+ size_t m_max_locals = 0;
+ size_t m_max_globaltemps = 0;
+ uint32_t m_first_common_local = 0;
+ uint32_t m_first_common_globaltemp = 0;
+
+ std::vector<const char*> m_filenames;
+ std::vector<qcint_t> m_filestrings;
+
+ // we cache the #IMMEDIATE string here
+ qcint_t m_str_immediate = 0;
+
+ // there should just be this one nil
+ ir_value *m_nil;
+ ir_value *m_reserved_va_count = nullptr;
+ ir_value *m_coverage_func = nullptr;
+
+ /* some virtual instructions require temps, and their code is isolated
+ * so that we don't need to keep track of their liveness.
+ */
+ ir_value *m_vinstr_temp[IR_MAX_VINSTR_TEMPS];