mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-20 18:32:01 +00:00
Merge branch 'cleanup' of github.com:graphitemaster/gmqcc into cleanup
This commit is contained in:
commit
90b5a6538a
18 changed files with 3639 additions and 3960 deletions
18
algo.h
Normal file
18
algo.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef GMQCC_ALGO_HDR
|
||||
#define GMQCC_ALGO_HDR
|
||||
|
||||
namespace algo {
|
||||
|
||||
template<typename ITER>
|
||||
void shiftback(ITER element, ITER end) {
|
||||
//typename ITER::value_type backup(move(*element)); // hold the element
|
||||
typename std::remove_reference<decltype(*element)>::type backup(move(*element)); // hold the element
|
||||
ITER p = element++;
|
||||
for (; element != end; p = element++)
|
||||
*p = move(*element);
|
||||
*p = move(backup);
|
||||
}
|
||||
|
||||
} // ::algo
|
||||
|
||||
#endif
|
440
ast.h
440
ast.h
|
@ -93,9 +93,7 @@ enum {
|
|||
TYPE_ast_state /* 22 */
|
||||
};
|
||||
|
||||
#define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) )
|
||||
#define ast_ctx(node) (((ast_node*)(node))->context)
|
||||
#define ast_side_effects(node) (((ast_node*)(node))->side_effects)
|
||||
#define ast_istype(x, t) ( (x)->m_node_type == (TYPE_##t) )
|
||||
|
||||
/* Node interface with common components
|
||||
*/
|
||||
|
@ -103,25 +101,32 @@ typedef void ast_node_delete(ast_node*);
|
|||
|
||||
struct ast_node
|
||||
{
|
||||
lex_ctx_t context;
|
||||
ast_node() = delete;
|
||||
ast_node(lex_ctx_t, int nodetype);
|
||||
virtual ~ast_node();
|
||||
|
||||
lex_ctx_t m_context;
|
||||
/* I don't feel comfortable using keywords like 'delete' as names... */
|
||||
ast_node_delete *destroy;
|
||||
int nodetype;
|
||||
/* keep: if a node contains this node, 'keep'
|
||||
int m_node_type;
|
||||
/* keep_node: if a node contains this node, 'keep_node'
|
||||
* prevents its dtor from destroying this node as well.
|
||||
*/
|
||||
bool keep;
|
||||
bool side_effects;
|
||||
bool m_keep_node;
|
||||
bool m_side_effects;
|
||||
|
||||
void propagate_side_effects(ast_node *other) const;
|
||||
};
|
||||
|
||||
#define ast_delete(x) (*( ((ast_node*)(x))->destroy ))((ast_node*)(x))
|
||||
#define ast_unref(x) do \
|
||||
{ \
|
||||
if (! (((ast_node*)(x))->keep) ) { \
|
||||
ast_delete(x); \
|
||||
} \
|
||||
#define ast_unref(x) do \
|
||||
{ \
|
||||
if (! (x)->m_keep_node ) { \
|
||||
delete (x); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
enum class ast_copy_type_t { value };
|
||||
static const ast_copy_type_t ast_copy_type = ast_copy_type_t::value;
|
||||
|
||||
/* Expression interface
|
||||
*
|
||||
* Any expression or block returns an ir_value, and needs
|
||||
|
@ -141,29 +146,37 @@ typedef bool ast_expression_codegen(ast_expression*,
|
|||
* type `expression`, so the ast_ident's codegen would search for
|
||||
* variables through the environment (or functions, constants...).
|
||||
*/
|
||||
struct ast_expression {
|
||||
ast_expression() {}
|
||||
struct ast_expression : ast_node {
|
||||
ast_expression() = delete;
|
||||
ast_expression(lex_ctx_t ctx, int nodetype, qc_type vtype);
|
||||
ast_expression(lex_ctx_t ctx, int nodetype);
|
||||
~ast_expression();
|
||||
ast_expression(ast_copy_type_t, int nodetype, const ast_expression&);
|
||||
ast_expression(ast_copy_type_t, const ast_expression&);
|
||||
|
||||
ast_node node;
|
||||
ast_expression_codegen *codegen;
|
||||
int vtype;
|
||||
ast_expression *next;
|
||||
static ast_expression *shallow_type(lex_ctx_t ctx, qc_type vtype);
|
||||
|
||||
bool compare_type(const ast_expression &other) const;
|
||||
void adopt_type(const ast_expression &other);
|
||||
|
||||
qc_type m_vtype = TYPE_VOID;
|
||||
ast_expression *m_next = nullptr;
|
||||
/* arrays get a member-count */
|
||||
size_t count;
|
||||
std::vector<ast_value*> params;
|
||||
size_t m_count = 0;
|
||||
std::vector<std::unique_ptr<ast_value>> m_type_params;
|
||||
|
||||
ast_flag_t flags;
|
||||
ast_flag_t m_flags = 0;
|
||||
/* void foo(string...) gets varparam set as a restriction
|
||||
* for variadic parameters
|
||||
*/
|
||||
ast_expression *varparam;
|
||||
ast_expression *m_varparam = nullptr;
|
||||
/* The codegen functions should store their output values
|
||||
* so we can call it multiple times without re-evaluating.
|
||||
* Store lvalue and rvalue seperately though. So that
|
||||
* ast_entfield for example can generate both if required.
|
||||
*/
|
||||
ir_value *outl;
|
||||
ir_value *outr;
|
||||
ir_value *m_outl = nullptr;
|
||||
ir_value *m_outr = nullptr;
|
||||
};
|
||||
|
||||
/* Value
|
||||
|
@ -174,72 +187,62 @@ struct ast_expression {
|
|||
* is like creating a 'float foo', foo serving as the type's name.
|
||||
*/
|
||||
union basic_value_t {
|
||||
qcfloat_t vfloat;
|
||||
int vint;
|
||||
vec3_t vvec;
|
||||
const char *vstring;
|
||||
int ventity;
|
||||
qcfloat_t vfloat;
|
||||
int vint;
|
||||
vec3_t vvec;
|
||||
const char *vstring;
|
||||
int ventity;
|
||||
ast_function *vfunc;
|
||||
ast_value *vfield;
|
||||
ast_value *vfield;
|
||||
};
|
||||
|
||||
struct ast_value
|
||||
struct ast_value : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_value() = delete;
|
||||
ast_value(lex_ctx_t ctx, const std::string &name, qc_type qctype);
|
||||
~ast_value();
|
||||
|
||||
const char *name;
|
||||
const char *desc;
|
||||
ast_value(ast_copy_type_t, const ast_expression&, const std::string&);
|
||||
ast_value(ast_copy_type_t, const ast_value&);
|
||||
ast_value(ast_copy_type_t, const ast_value&, const std::string&);
|
||||
|
||||
const char *argcounter;
|
||||
void add_param(ast_value*);
|
||||
|
||||
int cvq; /* const/var qualifier */
|
||||
bool isfield; /* this declares a field */
|
||||
bool isimm; /* an immediate, not just const */
|
||||
bool hasvalue;
|
||||
bool inexact; /* inexact coming from folded expression */
|
||||
basic_value_t constval;
|
||||
std::string m_name;
|
||||
std::string m_desc;
|
||||
|
||||
const char *m_argcounter = nullptr;
|
||||
|
||||
int m_cvq = CV_NONE; /* const/var qualifier */
|
||||
bool m_isfield = false; /* this declares a field */
|
||||
bool m_isimm = false; /* an immediate, not just const */
|
||||
bool m_hasvalue = false;
|
||||
bool m_inexact = false; /* inexact coming from folded expression */
|
||||
basic_value_t m_constval;
|
||||
/* for TYPE_ARRAY we have an optional vector
|
||||
* of constants when an initializer list
|
||||
* was provided.
|
||||
*/
|
||||
std::vector<basic_value_t> initlist;
|
||||
std::vector<basic_value_t> m_initlist;
|
||||
|
||||
/* usecount for the parser */
|
||||
size_t uses;
|
||||
size_t m_uses = 0;
|
||||
|
||||
ir_value *ir_v;
|
||||
ir_value **ir_values;
|
||||
size_t ir_value_count;
|
||||
ir_value *m_ir_v = nullptr;
|
||||
ir_value **m_ir_values = nullptr;
|
||||
size_t m_ir_value_count = 0;
|
||||
|
||||
/* ONLY for arrays in progs version up to 6 */
|
||||
ast_value *setter;
|
||||
ast_value *getter;
|
||||
ast_value *m_setter = nullptr;
|
||||
ast_value *m_getter = nullptr;
|
||||
|
||||
|
||||
bool intrinsic; /* true if associated with intrinsic */
|
||||
bool m_intrinsic = false; /* true if associated with intrinsic */
|
||||
};
|
||||
|
||||
ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int qctype);
|
||||
ast_value* ast_value_copy(const ast_value *self);
|
||||
/* This will NOT delete an underlying ast_function */
|
||||
void ast_value_delete(ast_value*);
|
||||
|
||||
bool ast_value_set_name(ast_value*, const char *name);
|
||||
|
||||
/*
|
||||
bool ast_value_codegen(ast_value*, ast_function*, bool lvalue, ir_value**);
|
||||
bool ast_local_codegen(ast_value *self, ir_function *func, bool isparam);
|
||||
*/
|
||||
|
||||
bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield);
|
||||
|
||||
void ast_value_params_add(ast_value*, ast_value*);
|
||||
|
||||
bool ast_compare_type(ast_expression *a, ast_expression *b);
|
||||
ast_expression* ast_type_copy(lex_ctx_t ctx, const ast_expression *ex);
|
||||
#define ast_type_adopt(a, b) ast_type_adopt_impl((ast_expression*)(a), (ast_expression*)(b))
|
||||
void ast_type_adopt_impl(ast_expression *self, const ast_expression *other);
|
||||
void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize);
|
||||
void ast_type_to_string(const ast_expression *e, char *buf, size_t bufsize);
|
||||
|
||||
enum ast_binary_ref {
|
||||
AST_REF_NONE = 0,
|
||||
|
@ -253,34 +256,36 @@ enum ast_binary_ref {
|
|||
*
|
||||
* A value-returning binary expression.
|
||||
*/
|
||||
struct ast_binary
|
||||
struct ast_binary : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
int op;
|
||||
ast_expression *left;
|
||||
ast_expression *right;
|
||||
ast_binary_ref refs;
|
||||
bool right_first;
|
||||
ast_binary() = delete;
|
||||
ast_binary(lex_ctx_t ctx, int op, ast_expression *l, ast_expression *r);
|
||||
~ast_binary();
|
||||
|
||||
int m_op;
|
||||
ast_expression *m_left;
|
||||
ast_expression *m_right;
|
||||
ast_binary_ref m_refs;
|
||||
bool m_right_first;
|
||||
};
|
||||
ast_binary* ast_binary_new(lex_ctx_t ctx,
|
||||
int op,
|
||||
ast_expression *left,
|
||||
ast_expression *right);
|
||||
|
||||
/* Binstore
|
||||
*
|
||||
* An assignment including a binary expression with the source as left operand.
|
||||
* Eg. a += b; is a binstore { INSTR_STORE, INSTR_ADD, a, b }
|
||||
*/
|
||||
struct ast_binstore
|
||||
struct ast_binstore : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
int opstore;
|
||||
int opbin;
|
||||
ast_expression *dest;
|
||||
ast_expression *source;
|
||||
ast_binstore() = delete;
|
||||
ast_binstore(lex_ctx_t ctx, int storeop, int mathop, ast_expression *l, ast_expression *r);
|
||||
~ast_binstore();
|
||||
|
||||
int m_opstore;
|
||||
int m_opbin;
|
||||
ast_expression *m_dest;
|
||||
ast_expression *m_source;
|
||||
/* for &~= which uses the destination in a binary in source we can use this */
|
||||
bool keep_dest;
|
||||
bool m_keep_dest;
|
||||
};
|
||||
ast_binstore* ast_binstore_new(lex_ctx_t ctx,
|
||||
int storeop,
|
||||
|
@ -292,15 +297,16 @@ ast_binstore* ast_binstore_new(lex_ctx_t ctx,
|
|||
*
|
||||
* Regular unary expressions: not,neg
|
||||
*/
|
||||
struct ast_unary
|
||||
struct ast_unary : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
int op;
|
||||
ast_expression *operand;
|
||||
ast_unary() = delete;
|
||||
~ast_unary();
|
||||
int m_op;
|
||||
ast_expression *m_operand;
|
||||
static ast_unary* make(lex_ctx_t ctx, int op, ast_expression *expr);
|
||||
private:
|
||||
ast_unary(lex_ctx_t ctx, int op, ast_expression *expr);
|
||||
};
|
||||
ast_unary* ast_unary_new(lex_ctx_t ctx,
|
||||
int op,
|
||||
ast_expression *expr);
|
||||
|
||||
/* Return
|
||||
*
|
||||
|
@ -308,13 +314,13 @@ ast_unary* ast_unary_new(lex_ctx_t ctx,
|
|||
* will refuse to create further instructions.
|
||||
* This should be honored by the parser.
|
||||
*/
|
||||
struct ast_return
|
||||
struct ast_return : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *operand;
|
||||
ast_return() = delete;
|
||||
ast_return(lex_ctx_t ctx, ast_expression *expr);
|
||||
~ast_return();
|
||||
ast_expression *m_operand;
|
||||
};
|
||||
ast_return* ast_return_new(lex_ctx_t ctx,
|
||||
ast_expression *expr);
|
||||
|
||||
/* Entity-field
|
||||
*
|
||||
|
@ -329,34 +335,37 @@ ast_return* ast_return_new(lex_ctx_t ctx,
|
|||
* For this we will have to extend the codegen() functions with
|
||||
* a flag saying whether or not we need an L or an R-value.
|
||||
*/
|
||||
struct ast_entfield
|
||||
struct ast_entfield : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
/* The entity can come from an expression of course. */
|
||||
ast_expression *entity;
|
||||
/* As can the field, it just must result in a value of TYPE_FIELD */
|
||||
ast_expression *field;
|
||||
ast_entfield() = delete;
|
||||
ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field);
|
||||
ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
|
||||
~ast_entfield();
|
||||
// The entity can come from an expression of course.
|
||||
ast_expression *m_entity;
|
||||
// As can the field, it just must result in a value of TYPE_FIELD
|
||||
ast_expression *m_field;
|
||||
};
|
||||
ast_entfield* ast_entfield_new(lex_ctx_t ctx, ast_expression *entity, ast_expression *field);
|
||||
ast_entfield* ast_entfield_new_force(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
|
||||
|
||||
/* Member access:
|
||||
*
|
||||
* For now used for vectors. If we get structs or unions
|
||||
* we can have them handled here as well.
|
||||
*/
|
||||
struct ast_member
|
||||
struct ast_member : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *owner;
|
||||
unsigned int field;
|
||||
const char *name;
|
||||
bool rvalue;
|
||||
};
|
||||
ast_member* ast_member_new(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const char *name);
|
||||
void ast_member_delete(ast_member*);
|
||||
bool ast_member_set_name(ast_member*, const char *name);
|
||||
static ast_member *make(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name);
|
||||
~ast_member();
|
||||
|
||||
ast_expression *m_owner;
|
||||
unsigned int m_field;
|
||||
std::string m_name;
|
||||
bool m_rvalue;
|
||||
|
||||
private:
|
||||
ast_member() = delete;
|
||||
ast_member(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name);
|
||||
};
|
||||
|
||||
/* Array index access:
|
||||
*
|
||||
|
@ -368,39 +377,43 @@ bool ast_member_set_name(ast_member*, const char *name);
|
|||
* In any case, accessing an element via a compiletime-constant index will
|
||||
* result in quick access to that variable.
|
||||
*/
|
||||
struct ast_array_index
|
||||
struct ast_array_index : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *array;
|
||||
ast_expression *index;
|
||||
static ast_array_index* make(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
|
||||
~ast_array_index();
|
||||
ast_expression *m_array;
|
||||
ast_expression *m_index;
|
||||
private:
|
||||
ast_array_index() = delete;
|
||||
ast_array_index(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
|
||||
};
|
||||
ast_array_index* ast_array_index_new(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
|
||||
|
||||
/* Vararg pipe node:
|
||||
*
|
||||
* copy all varargs starting from a specific index
|
||||
*/
|
||||
struct ast_argpipe
|
||||
struct ast_argpipe : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *index;
|
||||
ast_argpipe() = delete;
|
||||
ast_argpipe(lex_ctx_t ctx, ast_expression *index);
|
||||
~ast_argpipe();
|
||||
ast_expression *m_index;
|
||||
};
|
||||
ast_argpipe* ast_argpipe_new(lex_ctx_t ctx, ast_expression *index);
|
||||
|
||||
/* Store
|
||||
*
|
||||
* Stores left<-right and returns left.
|
||||
* Specialized binary expression node
|
||||
*/
|
||||
struct ast_store
|
||||
struct ast_store : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
int op;
|
||||
ast_expression *dest;
|
||||
ast_expression *source;
|
||||
ast_store() = delete;
|
||||
ast_store(lex_ctx_t ctx, int op, ast_expression *d, ast_expression *s);
|
||||
~ast_store();
|
||||
int m_op;
|
||||
ast_expression *m_dest;
|
||||
ast_expression *m_source;
|
||||
};
|
||||
ast_store* ast_store_new(lex_ctx_t ctx, int op,
|
||||
ast_expression *d, ast_expression *s);
|
||||
|
||||
/* If
|
||||
*
|
||||
|
@ -413,15 +426,16 @@ ast_store* ast_store_new(lex_ctx_t ctx, int op,
|
|||
* output field though. For ternary expressions an ast_ternary will be
|
||||
* added.
|
||||
*/
|
||||
struct ast_ifthen
|
||||
struct ast_ifthen : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *cond;
|
||||
ast_ifthen() = delete;
|
||||
ast_ifthen(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
|
||||
~ast_ifthen();
|
||||
ast_expression *m_cond;
|
||||
/* It's all just 'expressions', since an ast_block is one too. */
|
||||
ast_expression *on_true;
|
||||
ast_expression *on_false;
|
||||
ast_expression *m_on_true;
|
||||
ast_expression *m_on_false;
|
||||
};
|
||||
ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
|
||||
|
||||
/* Ternary expressions...
|
||||
*
|
||||
|
@ -436,15 +450,16 @@ ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *
|
|||
* This is the only ast_node beside ast_value which contains
|
||||
* an ir_value. Theoretically we don't need to remember it though.
|
||||
*/
|
||||
struct ast_ternary
|
||||
struct ast_ternary : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *cond;
|
||||
ast_ternary() = delete;
|
||||
ast_ternary(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
|
||||
~ast_ternary();
|
||||
ast_expression *m_cond;
|
||||
/* It's all just 'expressions', since an ast_block is one too. */
|
||||
ast_expression *on_true;
|
||||
ast_expression *on_false;
|
||||
ast_expression *m_on_true;
|
||||
ast_expression *m_on_false;
|
||||
};
|
||||
ast_ternary* ast_ternary_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
|
||||
|
||||
/* A general loop node
|
||||
*
|
||||
|
@ -469,37 +484,37 @@ continue: // a 'continue' will jump here
|
|||
{inc};
|
||||
}
|
||||
*/
|
||||
struct ast_loop
|
||||
struct ast_loop : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *initexpr;
|
||||
ast_expression *precond;
|
||||
ast_expression *postcond;
|
||||
ast_expression *increment;
|
||||
ast_expression *body;
|
||||
ast_loop() = delete;
|
||||
ast_loop(lex_ctx_t ctx,
|
||||
ast_expression *initexpr,
|
||||
ast_expression *precond, bool pre_not,
|
||||
ast_expression *postcond, bool post_not,
|
||||
ast_expression *increment,
|
||||
ast_expression *body);
|
||||
~ast_loop();
|
||||
ast_expression *m_initexpr;
|
||||
ast_expression *m_precond;
|
||||
ast_expression *m_postcond;
|
||||
ast_expression *m_increment;
|
||||
ast_expression *m_body;
|
||||
/* For now we allow a seperate flag on whether or not the condition
|
||||
* is supposed to be true or false.
|
||||
* That way, the parser can generate a 'while not(!x)' for `while(x)`
|
||||
* if desired, which is useful for the new -f{true,false}-empty-strings
|
||||
* flag.
|
||||
*/
|
||||
bool pre_not;
|
||||
bool post_not;
|
||||
bool m_pre_not;
|
||||
bool m_post_not;
|
||||
};
|
||||
ast_loop* ast_loop_new(lex_ctx_t ctx,
|
||||
ast_expression *initexpr,
|
||||
ast_expression *precond, bool pre_not,
|
||||
ast_expression *postcond, bool post_not,
|
||||
ast_expression *increment,
|
||||
ast_expression *body);
|
||||
|
||||
/* Break/Continue
|
||||
*/
|
||||
struct ast_breakcont
|
||||
struct ast_breakcont : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
bool is_continue;
|
||||
unsigned int levels;
|
||||
bool m_is_continue;
|
||||
unsigned int m_levels;
|
||||
};
|
||||
ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels);
|
||||
|
||||
|
@ -514,15 +529,14 @@ ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels
|
|||
* TODO: Ticket #20
|
||||
*/
|
||||
struct ast_switch_case {
|
||||
ast_expression *value; /* #20 will replace this */
|
||||
ast_expression *code;
|
||||
ast_expression *m_value; /* #20 will replace this */
|
||||
ast_expression *m_code;
|
||||
};
|
||||
|
||||
struct ast_switch
|
||||
struct ast_switch : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *operand;
|
||||
std::vector<ast_switch_case> cases;
|
||||
ast_expression *m_operand;
|
||||
std::vector<ast_switch_case> m_cases;
|
||||
};
|
||||
|
||||
ast_switch* ast_switch_new(lex_ctx_t ctx, ast_expression *op);
|
||||
|
@ -531,15 +545,14 @@ ast_switch* ast_switch_new(lex_ctx_t ctx, ast_expression *op);
|
|||
*
|
||||
* Introduce a label which can be used together with 'goto'
|
||||
*/
|
||||
struct ast_label
|
||||
struct ast_label : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
const char *name;
|
||||
ir_block *irblock;
|
||||
std::vector<ast_goto*> gotos;
|
||||
const char *m_name;
|
||||
ir_block *m_irblock;
|
||||
std::vector<ast_goto*> m_gotos;
|
||||
|
||||
/* means it has not yet been defined */
|
||||
bool undefined;
|
||||
bool m_undefined;
|
||||
};
|
||||
|
||||
ast_label* ast_label_new(lex_ctx_t ctx, const char *name, bool undefined);
|
||||
|
@ -548,12 +561,11 @@ ast_label* ast_label_new(lex_ctx_t ctx, const char *name, bool undefined);
|
|||
*
|
||||
* Go to a label, the label node is filled in at a later point!
|
||||
*/
|
||||
struct ast_goto
|
||||
struct ast_goto : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
const char *name;
|
||||
ast_label *target;
|
||||
ir_block *irblock_from;
|
||||
const char *m_name;
|
||||
ast_label *m_target;
|
||||
ir_block *m_irblock_from;
|
||||
};
|
||||
|
||||
ast_goto* ast_goto_new(lex_ctx_t ctx, const char *name);
|
||||
|
@ -563,11 +575,10 @@ void ast_goto_set_label(ast_goto*, ast_label*);
|
|||
*
|
||||
* For frame/think state updates: void foo() [framenum, nextthink] {}
|
||||
*/
|
||||
struct ast_state
|
||||
struct ast_state : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *framenum;
|
||||
ast_expression *nextthink;
|
||||
ast_expression *m_framenum;
|
||||
ast_expression *m_nextthink;
|
||||
};
|
||||
ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think);
|
||||
void ast_state_delete(ast_state*);
|
||||
|
@ -582,12 +593,11 @@ void ast_state_delete(ast_state*);
|
|||
* Additionally it contains a list of ast_expressions as parameters.
|
||||
* Since calls can return values, an ast_call is also an ast_expression.
|
||||
*/
|
||||
struct ast_call
|
||||
struct ast_call : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
ast_expression *func;
|
||||
std::vector<ast_expression *> params;
|
||||
ast_expression *va_count;
|
||||
ast_expression *m_func;
|
||||
std::vector<ast_expression *> m_params;
|
||||
ast_expression *m_va_count;
|
||||
};
|
||||
ast_call* ast_call_new(lex_ctx_t ctx,
|
||||
ast_expression *funcexpr);
|
||||
|
@ -596,13 +606,11 @@ bool ast_call_check_types(ast_call*, ast_expression *this_func_va_type);
|
|||
/* Blocks
|
||||
*
|
||||
*/
|
||||
struct ast_block
|
||||
struct ast_block : ast_expression
|
||||
{
|
||||
ast_expression expression;
|
||||
|
||||
std::vector<ast_value*> locals;
|
||||
std::vector<ast_expression*> exprs;
|
||||
std::vector<ast_expression*> collect;
|
||||
std::vector<ast_value*> m_locals;
|
||||
std::vector<ast_expression*> m_exprs;
|
||||
std::vector<ast_expression*> m_collect;
|
||||
};
|
||||
ast_block* ast_block_new(lex_ctx_t ctx);
|
||||
void ast_block_delete(ast_block*);
|
||||
|
@ -621,39 +629,37 @@ bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
|
|||
* pointers could just work with a name. However, this way could be
|
||||
* more flexible, and adds no real complexity.
|
||||
*/
|
||||
struct ast_function
|
||||
struct ast_function : ast_node
|
||||
{
|
||||
ast_node node;
|
||||
ast_value *m_function_type;
|
||||
const char *m_name;
|
||||
|
||||
ast_value *vtype;
|
||||
const char *name;
|
||||
|
||||
int builtin;
|
||||
int m_builtin;
|
||||
|
||||
/* list of used-up names for statics without the count suffix */
|
||||
std::vector<char*> static_names;
|
||||
std::vector<char*> m_static_names;
|
||||
/* number of static variables, by convention this includes the
|
||||
* ones without the count-suffix - remember this when dealing
|
||||
* with savegames. uint instead of size_t as %zu in printf is
|
||||
* C99, so no windows support. */
|
||||
unsigned int static_count;
|
||||
unsigned int m_static_count;
|
||||
|
||||
ir_function *ir_func;
|
||||
ir_block *curblock;
|
||||
std::vector<ir_block*> breakblocks;
|
||||
std::vector<ir_block*> continueblocks;
|
||||
ir_function *m_ir_func;
|
||||
ir_block *m_curblock;
|
||||
std::vector<ir_block*> m_breakblocks;
|
||||
std::vector<ir_block*> m_continueblocks;
|
||||
|
||||
size_t labelcount;
|
||||
size_t m_labelcount;
|
||||
/* in order for thread safety - for the optional
|
||||
* channel abesed multithreading... keeping a buffer
|
||||
* here to use in ast_function_label.
|
||||
*/
|
||||
char labelbuf[64];
|
||||
std::vector<ast_block*> blocks;
|
||||
ast_value *varargs;
|
||||
ast_value *argc;
|
||||
ast_value *fixedparams;
|
||||
ast_value *return_value;
|
||||
char m_labelbuf[64];
|
||||
std::vector<std::unique_ptr<ast_block>> m_blocks;
|
||||
ast_value *m_varargs;
|
||||
ast_value *m_argc;
|
||||
ast_value *m_fixedparams;
|
||||
ast_value *m_return_value;
|
||||
};
|
||||
ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype);
|
||||
/* This will NOT delete the underlying ast_value */
|
||||
|
|
47
code.cpp
47
code.cpp
|
@ -106,35 +106,39 @@ void code_pop_statement(code_t *code)
|
|||
code->columnnums.pop_back();
|
||||
}
|
||||
|
||||
code_t *code_init() {
|
||||
void *code_t::operator new(std::size_t bytes) {
|
||||
return mem_a(bytes);
|
||||
}
|
||||
|
||||
void code_t::operator delete(void *ptr) {
|
||||
mem_d(ptr);
|
||||
}
|
||||
|
||||
code_t::code_t()
|
||||
{
|
||||
static lex_ctx_t empty_ctx = {0, 0, 0};
|
||||
static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
|
||||
static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
|
||||
static prog_section_def_t empty_def = {0, 0, 0};
|
||||
|
||||
code_t *code = (code_t*)mem_a(sizeof(code_t));
|
||||
int i = 0;
|
||||
string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
|
||||
|
||||
memset(code, 0, sizeof(code_t));
|
||||
code->entfields = 0;
|
||||
code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
|
||||
// The way progs.dat is suppose to work is odd, there needs to be
|
||||
// some null (empty) statements, functions, and 28 globals
|
||||
globals.insert(globals.begin(), 28, 0);
|
||||
|
||||
/*
|
||||
* The way progs.dat is suppose to work is odd, there needs to be
|
||||
* some null (empty) statements, functions, and 28 globals
|
||||
*/
|
||||
for(; i < 28; i++)
|
||||
code->globals.push_back(0);
|
||||
chars.push_back('\0');
|
||||
functions.push_back(empty_function);
|
||||
|
||||
code->chars.push_back('\0');
|
||||
code->functions.push_back(empty_function);
|
||||
code_push_statement(this, &empty_statement, empty_ctx);
|
||||
|
||||
code_push_statement(code, &empty_statement, empty_ctx);
|
||||
defs.push_back(empty_def);
|
||||
fields.push_back(empty_def);
|
||||
}
|
||||
|
||||
code->defs.push_back(empty_def);
|
||||
code->fields.push_back(empty_def);
|
||||
|
||||
return code;
|
||||
code_t::~code_t()
|
||||
{
|
||||
util_htdel(string_cache);
|
||||
}
|
||||
|
||||
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
|
||||
|
@ -342,8 +346,3 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
|
|||
code_stats(filename, lnofile, code, &code_header);
|
||||
return true;
|
||||
}
|
||||
|
||||
void code_cleanup(code_t *code) {
|
||||
util_htdel(code->string_cache);
|
||||
mem_d(code);
|
||||
}
|
||||
|
|
102
fold.cpp
102
fold.cpp
|
@ -535,10 +535,10 @@ static GMQCC_INLINE void sfloat_init(sfloat_state_t *state) {
|
|||
* This file is thus, split into two parts.
|
||||
*/
|
||||
|
||||
#define isfloat(X) (((ast_expression*)(X))->vtype == TYPE_FLOAT)
|
||||
#define isvector(X) (((ast_expression*)(X))->vtype == TYPE_VECTOR)
|
||||
#define isstring(X) (((ast_expression*)(X))->vtype == TYPE_STRING)
|
||||
#define isarray(X) (((ast_expression*)(X))->vtype == TYPE_ARRAY)
|
||||
#define isfloat(X) (((ast_expression*)(X))->m_vtype == TYPE_FLOAT)
|
||||
#define isvector(X) (((ast_expression*)(X))->m_vtype == TYPE_VECTOR)
|
||||
#define isstring(X) (((ast_expression*)(X))->m_vtype == TYPE_STRING)
|
||||
#define isarray(X) (((ast_expression*)(X))->m_vtype == TYPE_ARRAY)
|
||||
#define isfloats(X,Y) (isfloat (X) && isfloat (Y))
|
||||
|
||||
/*
|
||||
|
@ -858,15 +858,15 @@ end:
|
|||
}
|
||||
|
||||
qcfloat_t fold::immvalue_float(ast_value *value) {
|
||||
return value->constval.vfloat;
|
||||
return value->m_constval.vfloat;
|
||||
}
|
||||
|
||||
vec3_t fold::immvalue_vector(ast_value *value) {
|
||||
return value->constval.vvec;
|
||||
return value->m_constval.vvec;
|
||||
}
|
||||
|
||||
const char *fold::immvalue_string(ast_value *value) {
|
||||
return value->constval.vstring;
|
||||
return value->m_constval.vstring;
|
||||
}
|
||||
|
||||
lex_ctx_t fold::ctx() {
|
||||
|
@ -878,32 +878,32 @@ lex_ctx_t fold::ctx() {
|
|||
}
|
||||
|
||||
bool fold::immediate_true(ast_value *v) {
|
||||
switch (v->expression.vtype) {
|
||||
switch (v->m_vtype) {
|
||||
case TYPE_FLOAT:
|
||||
return !!v->constval.vfloat;
|
||||
return !!v->m_constval.vfloat;
|
||||
case TYPE_INTEGER:
|
||||
return !!v->constval.vint;
|
||||
return !!v->m_constval.vint;
|
||||
case TYPE_VECTOR:
|
||||
if (OPTS_FLAG(CORRECT_LOGIC))
|
||||
return vec3_pbool(v->constval.vvec);
|
||||
return !!(v->constval.vvec.x);
|
||||
return vec3_pbool(v->m_constval.vvec);
|
||||
return !!(v->m_constval.vvec.x);
|
||||
case TYPE_STRING:
|
||||
if (!v->constval.vstring)
|
||||
if (!v->m_constval.vstring)
|
||||
return false;
|
||||
if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
|
||||
return true;
|
||||
return !!v->constval.vstring[0];
|
||||
return !!v->m_constval.vstring[0];
|
||||
default:
|
||||
compile_error(ctx(), "internal error: fold_immediate_true on invalid type");
|
||||
break;
|
||||
}
|
||||
return !!v->constval.vfunc;
|
||||
return !!v->m_constval.vfunc;
|
||||
}
|
||||
|
||||
/* Handy macros to determine if an ast_value can be constant folded. */
|
||||
#define fold_can_1(X) \
|
||||
(ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \
|
||||
((ast_expression*)(X))->vtype != TYPE_FUNCTION)
|
||||
(ast_istype(((ast_expression*)(X)), ast_value) && (X)->m_hasvalue && ((X)->m_cvq == CV_CONST) && \
|
||||
((ast_expression*)(X))->m_vtype != TYPE_FUNCTION)
|
||||
|
||||
#define fold_can_2(X, Y) (fold_can_1(X) && fold_can_1(Y))
|
||||
|
||||
|
@ -938,8 +938,8 @@ bool fold::generate(ir_builder *ir) {
|
|||
if (!ast_global_codegen((cur = it), ir, false)) goto err;
|
||||
return true;
|
||||
err:
|
||||
con_out("failed to generate global %s\n", cur->name);
|
||||
ir_builder_delete(ir);
|
||||
con_out("failed to generate global %s\n", cur->m_name);
|
||||
delete ir;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -957,14 +957,14 @@ fold::~fold() {
|
|||
|
||||
ast_expression *fold::constgen_float(qcfloat_t value, bool inexact) {
|
||||
for (auto &it : m_imm_float)
|
||||
if (!memcmp(&it->constval.vfloat, &value, sizeof(qcfloat_t)))
|
||||
if (!memcmp(&it->m_constval.vfloat, &value, sizeof(qcfloat_t)))
|
||||
return (ast_expression*)it;
|
||||
|
||||
ast_value *out = ast_value_new(ctx(), "#IMMEDIATE", TYPE_FLOAT);
|
||||
out->cvq = CV_CONST;
|
||||
out->hasvalue = true;
|
||||
out->inexact = inexact;
|
||||
out->constval.vfloat = value;
|
||||
out->m_cvq = CV_CONST;
|
||||
out->m_hasvalue = true;
|
||||
out->m_inexact = inexact;
|
||||
out->m_constval.vfloat = value;
|
||||
|
||||
m_imm_float.push_back(out);
|
||||
|
||||
|
@ -973,13 +973,13 @@ ast_expression *fold::constgen_float(qcfloat_t value, bool inexact) {
|
|||
|
||||
ast_expression *fold::constgen_vector(vec3_t value) {
|
||||
for (auto &it : m_imm_vector)
|
||||
if (vec3_cmp(it->constval.vvec, value))
|
||||
if (vec3_cmp(it->m_constval.vvec, value))
|
||||
return (ast_expression*)it;
|
||||
|
||||
ast_value *out = ast_value_new(ctx(), "#IMMEDIATE", TYPE_VECTOR);
|
||||
out->cvq = CV_CONST;
|
||||
out->hasvalue = true;
|
||||
out->constval.vvec = value;
|
||||
out->m_cvq = CV_CONST;
|
||||
out->m_hasvalue = true;
|
||||
out->m_constval.vvec = value;
|
||||
|
||||
m_imm_vector.push_back(out);
|
||||
|
||||
|
@ -998,15 +998,15 @@ ast_expression *fold::constgen_string(const char *str, bool translate) {
|
|||
char name[32];
|
||||
util_snprintf(name, sizeof(name), "dotranslate_%zu", m_parser->translated++);
|
||||
out = ast_value_new(ctx(), name, TYPE_STRING);
|
||||
out->expression.flags |= AST_FLAG_INCLUDE_DEF; /* def needs to be included for translatables */
|
||||
out->m_flags |= AST_FLAG_INCLUDE_DEF; /* def needs to be included for translatables */
|
||||
} else {
|
||||
out = ast_value_new(ctx(), "#IMMEDIATE", TYPE_STRING);
|
||||
}
|
||||
|
||||
out->cvq = CV_CONST;
|
||||
out->hasvalue = true;
|
||||
out->isimm = true;
|
||||
out->constval.vstring = parser_strdup(str);
|
||||
out->m_cvq = CV_CONST;
|
||||
out->m_hasvalue = true;
|
||||
out->m_isimm = true;
|
||||
out->m_constval.vstring = parser_strdup(str);
|
||||
|
||||
m_imm_string.push_back(out);
|
||||
util_htseth(table, str, hash, out);
|
||||
|
@ -1057,7 +1057,7 @@ inexact_possible:
|
|||
bool fold::check_inexact_float(ast_value *a, ast_value *b) {
|
||||
if (!OPTS_WARN(WARN_INEXACT_COMPARES))
|
||||
return false;
|
||||
if (!a->inexact && !b->inexact)
|
||||
if (!a->m_inexact && !b->m_inexact)
|
||||
return false;
|
||||
return compile_warning(ctx(), WARN_INEXACT_COMPARES, "inexact value in comparison");
|
||||
}
|
||||
|
@ -1070,8 +1070,8 @@ ast_expression *fold::op_mul_vec(vec3_t vec, ast_value *sel, const char *set) {
|
|||
ast_expression *out;
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx(), (ast_expression*)sel, set[0]-'x', nullptr);
|
||||
out->node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
out->m_keep_node = false;
|
||||
((ast_member*)out)->m_rvalue = true;
|
||||
if (x != -1.0f)
|
||||
return (ast_expression*)ast_binary_new(ctx(), INSTR_MUL_F, constgen_float(x, false), out);
|
||||
}
|
||||
|
@ -1374,7 +1374,7 @@ ast_expression *fold::op_length(ast_value *a) {
|
|||
if (fold_can_1(a) && isstring(a))
|
||||
return constgen_float(strlen(immvalue_string(a)), false);
|
||||
if (isarray(a))
|
||||
return constgen_float(a->initlist.size(), false);
|
||||
return constgen_float(a->m_initlist.size(), false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1526,18 +1526,18 @@ ast_expression *fold::intrinsic(const char *intrinsic, ast_expression **arg) {
|
|||
#undef fold_can_1
|
||||
#undef fold_can_2
|
||||
|
||||
#define isfloat(X) ((X)->vtype == TYPE_FLOAT)
|
||||
/*#define isstring(X) ((X)->vtype == TYPE_STRING)*/
|
||||
/*#define isvector(X) ((X)->vtype == TYPE_VECTOR)*/
|
||||
#define fold_can_1(X) ((X)->hasvalue && (X)->cvq == CV_CONST)
|
||||
#define isfloat(X) ((X)->m_vtype == TYPE_FLOAT)
|
||||
/*#define isstring(X) ((X)->m_vtype == TYPE_STRING)*/
|
||||
/*#define isvector(X) ((X)->m_vtype == TYPE_VECTOR)*/
|
||||
#define fold_can_1(X) ((X)->m_hasvalue && (X)->m_cvq == CV_CONST)
|
||||
/*#define fold_can_2(X,Y) (fold_can_1(X) && fold_can_1(Y))*/
|
||||
|
||||
qcfloat_t fold::immvalue_float(ir_value *value) {
|
||||
return value->constval.vfloat;
|
||||
return value->m_constval.vfloat;
|
||||
}
|
||||
|
||||
vec3_t fold::immvalue_vector(ir_value *value) {
|
||||
return value->constval.vvec;
|
||||
return value->m_constval.vvec;
|
||||
}
|
||||
|
||||
ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, int op) {
|
||||
|
@ -1612,10 +1612,10 @@ int fold::cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
|
|||
ast_expression_codegen *cgen;
|
||||
ir_block *elide;
|
||||
ir_value *dummy;
|
||||
bool istrue = (immvalue_float(condval) != 0.0f && branch->on_true);
|
||||
bool isfalse = (immvalue_float(condval) == 0.0f && branch->on_false);
|
||||
ast_expression *path = (istrue) ? branch->on_true :
|
||||
(isfalse) ? branch->on_false : nullptr;
|
||||
bool istrue = (immvalue_float(condval) != 0.0f && branch->m_on_true);
|
||||
bool isfalse = (immvalue_float(condval) == 0.0f && branch->m_on_false);
|
||||
ast_expression *path = (istrue) ? branch->m_on_true :
|
||||
(isfalse) ? branch->m_on_false : nullptr;
|
||||
if (!path) {
|
||||
/*
|
||||
* no path to take implies that the evaluation is if(0) and there
|
||||
|
@ -1625,17 +1625,17 @@ int fold::cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!(elide = ir_function_create_block(ast_ctx(branch), func->ir_func, ast_function_label(func, ((istrue) ? "ontrue" : "onfalse")))))
|
||||
if (!(elide = ir_function_create_block(branch->m_context, func->m_ir_func, ast_function_label(func, ((istrue) ? "ontrue" : "onfalse")))))
|
||||
return false;
|
||||
if (!(*(cgen = path->codegen))((ast_expression*)path, func, false, &dummy))
|
||||
if (!(*(cgen = path->m_codegen))((ast_expression*)path, func, false, &dummy))
|
||||
return false;
|
||||
if (!ir_block_create_jump(func->curblock, ast_ctx(branch), elide))
|
||||
if (!ir_block_create_jump(func->m_curblock, branch->m_context, elide))
|
||||
return false;
|
||||
/*
|
||||
* now the branch has been eliminated and the correct block for the constant evaluation
|
||||
* is expanded into the current block for the function.
|
||||
*/
|
||||
func->curblock = elide;
|
||||
func->m_curblock = elide;
|
||||
++opts_optimizationcount[OPTIM_CONST_FOLD_DCE];
|
||||
return true;
|
||||
}
|
||||
|
|
18
gmqcc.h
18
gmqcc.h
|
@ -1,6 +1,10 @@
|
|||
#ifndef GMQCC_HDR
|
||||
#define GMQCC_HDR
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
using std::move;
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -259,7 +263,7 @@ int util_getline(char **, size_t *, FILE *);
|
|||
/* code.c */
|
||||
|
||||
/* Note: if you change the order, fix type_sizeof in ir.c */
|
||||
enum {
|
||||
enum qc_type {
|
||||
TYPE_VOID ,
|
||||
TYPE_STRING ,
|
||||
TYPE_FLOAT ,
|
||||
|
@ -498,6 +502,10 @@ typedef int32_t qcint_t;
|
|||
typedef uint32_t qcuint_t;
|
||||
|
||||
struct code_t {
|
||||
void* operator new(std::size_t);
|
||||
void operator delete(void*);
|
||||
code_t();
|
||||
~code_t();
|
||||
std::vector<prog_section_statement_t> statements;
|
||||
std::vector<int> linenums;
|
||||
std::vector<int> columnnums;
|
||||
|
@ -506,10 +514,10 @@ struct code_t {
|
|||
std::vector<prog_section_function_t> functions;
|
||||
std::vector<int> globals;
|
||||
std::vector<char> chars;
|
||||
uint16_t crc;
|
||||
uint32_t entfields;
|
||||
uint16_t crc = 0;
|
||||
uint32_t entfields = 0;
|
||||
ht string_cache;
|
||||
qcint_t string_cached_empty;
|
||||
qcint_t string_cached_empty = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -589,7 +597,7 @@ void compile_show_werrors(void);
|
|||
|
||||
/* ir.c */
|
||||
/* TODO: cleanup */
|
||||
enum store_types {
|
||||
enum store_type {
|
||||
store_global,
|
||||
store_local, /* local, assignable for now, should get promoted later */
|
||||
store_param, /* parameters, they are locals with a fixed position */
|
||||
|
|
360
intrin.cpp
360
intrin.cpp
File diff suppressed because it is too large
Load diff
2
intrin.h
2
intrin.h
|
@ -30,7 +30,7 @@ struct intrin {
|
|||
|
||||
protected:
|
||||
lex_ctx_t ctx() const;
|
||||
ast_function *value(ast_value **out, const char *name, qcint_t vtype);
|
||||
ast_function *value(ast_value **out, const char *name, qc_type vtype);
|
||||
void reg(ast_value *const value, ast_function *const func);
|
||||
|
||||
ast_expression *nullfunc();
|
||||
|
|
221
ir.h
221
ir.h
|
@ -36,29 +36,33 @@ enum {
|
|||
};
|
||||
|
||||
struct ir_value {
|
||||
char *name;
|
||||
int vtype;
|
||||
int store;
|
||||
lex_ctx_t context;
|
||||
int fieldtype; // even the IR knows the subtype of a field
|
||||
int outtype; // and the output type of a function
|
||||
int cvq; // 'const' vs 'var' qualifier
|
||||
ir_flag_t flags;
|
||||
ir_value(std::string&& name, store_type storetype, qc_type vtype);
|
||||
~ir_value();
|
||||
|
||||
std::vector<ir_instr *> reads;
|
||||
std::vector<ir_instr *> writes;
|
||||
std::string m_name;
|
||||
|
||||
qc_type m_vtype;
|
||||
store_type m_store;
|
||||
lex_ctx_t m_context;
|
||||
qc_type m_fieldtype; // even the IR knows the subtype of a field
|
||||
qc_type m_outtype; // and the output type of a function
|
||||
int m_cvq; // 'const' vs 'var' qualifier
|
||||
ir_flag_t m_flags;
|
||||
|
||||
std::vector<ir_instr *> m_reads;
|
||||
std::vector<ir_instr *> m_writes;
|
||||
|
||||
// constant values
|
||||
bool hasvalue;
|
||||
bool m_hasvalue;
|
||||
union {
|
||||
qcfloat_t vfloat;
|
||||
int vint;
|
||||
vec3_t vvec;
|
||||
int32_t ivec[3];
|
||||
char *vstring;
|
||||
ir_value *vpointer;
|
||||
qcfloat_t vfloat;
|
||||
int vint;
|
||||
vec3_t vvec;
|
||||
int32_t ivec[3];
|
||||
char *vstring;
|
||||
ir_value *vpointer;
|
||||
ir_function *vfunc;
|
||||
} constval;
|
||||
} m_constval;
|
||||
|
||||
struct {
|
||||
int32_t globaladdr;
|
||||
|
@ -66,17 +70,17 @@ struct ir_value {
|
|||
int32_t local; // filled by the local-allocator
|
||||
int32_t addroffset; // added for members
|
||||
int32_t fieldaddr; // to generate field-addresses early
|
||||
} code;
|
||||
} m_code;
|
||||
|
||||
// for accessing vectors
|
||||
ir_value *members[3];
|
||||
ir_value *memberof;
|
||||
ir_value *m_members[3];
|
||||
ir_value *m_memberof;
|
||||
|
||||
bool unique_life; // arrays will never overlap with temps
|
||||
bool locked; // temps living during a CALL must be locked
|
||||
bool callparam;
|
||||
bool m_unique_life; // arrays will never overlap with temps
|
||||
bool m_locked; // temps living during a CALL must be locked
|
||||
bool m_callparam;
|
||||
|
||||
ir_life_entry_t *life; // For the temp allocator
|
||||
std::vector<ir_life_entry_t> m_life; // For the temp allocator
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,7 +88,6 @@ struct ir_value {
|
|||
* if a result of an operation: the function should store
|
||||
* it to remember to delete it / garbage collect it
|
||||
*/
|
||||
void ir_value_delete(ir_value*);
|
||||
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
|
||||
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
|
||||
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
|
||||
|
@ -102,57 +105,63 @@ struct ir_phi_entry_t {
|
|||
|
||||
/* instruction */
|
||||
struct ir_instr {
|
||||
int opcode;
|
||||
lex_ctx_t context;
|
||||
ir_value *(_ops[3]);
|
||||
ir_block *(bops[2]);
|
||||
ir_instr(lex_ctx_t, ir_block *owner, int opcode);
|
||||
~ir_instr();
|
||||
|
||||
std::vector<ir_phi_entry_t> phi;
|
||||
std::vector<ir_value *> params;
|
||||
int m_opcode;
|
||||
lex_ctx_t m_context;
|
||||
ir_value *(_m_ops[3]) = { nullptr, nullptr, nullptr };
|
||||
ir_block *(m_bops[2]) = { nullptr, nullptr };
|
||||
|
||||
std::vector<ir_phi_entry_t> m_phi;
|
||||
std::vector<ir_value *> m_params;
|
||||
|
||||
// For the temp-allocation
|
||||
size_t eid;
|
||||
size_t m_eid = 0;
|
||||
|
||||
// For IFs
|
||||
bool likely;
|
||||
bool m_likely = true;
|
||||
|
||||
ir_block *owner;
|
||||
ir_block *m_owner;
|
||||
};
|
||||
|
||||
/* block */
|
||||
struct ir_block {
|
||||
char *label;
|
||||
lex_ctx_t context;
|
||||
bool final; /* once a jump is added we're done */
|
||||
ir_block(ir_function *owner, const std::string& name);
|
||||
~ir_block();
|
||||
|
||||
ir_instr **instr;
|
||||
ir_block **entries;
|
||||
ir_block **exits;
|
||||
std::vector<ir_value *> living;
|
||||
ir_function *m_owner;
|
||||
std::string m_label;
|
||||
|
||||
lex_ctx_t m_context;
|
||||
bool m_final = false; /* once a jump is added we're done */
|
||||
|
||||
ir_instr **m_instr = nullptr;
|
||||
ir_block **m_entries = nullptr;
|
||||
ir_block **m_exits = nullptr;
|
||||
std::vector<ir_value *> m_living;
|
||||
|
||||
/* For the temp-allocation */
|
||||
size_t entry_id;
|
||||
size_t eid;
|
||||
bool is_return;
|
||||
size_t m_entry_id = 0;
|
||||
size_t m_eid = 0;
|
||||
bool m_is_return = false;
|
||||
|
||||
ir_function *owner;
|
||||
|
||||
bool generated;
|
||||
size_t code_start;
|
||||
bool m_generated = false;
|
||||
size_t m_code_start = 0;
|
||||
};
|
||||
|
||||
ir_value* ir_block_create_binop(ir_block*, lex_ctx_t, const char *label, int op, ir_value *left, ir_value *right);
|
||||
ir_value* ir_block_create_unary(ir_block*, lex_ctx_t, const char *label, int op, ir_value *operand);
|
||||
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx_t, int op, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx_t, ir_value *target, ir_value *what);
|
||||
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx_t, const char *label, ir_value *ent, ir_value *field, int outype);
|
||||
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx_t, const char *label, ir_value *ent, ir_value *field, qc_type outype);
|
||||
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx_t, const char *label, ir_value *entity, ir_value *field);
|
||||
bool GMQCC_WARN ir_block_create_state_op(ir_block*, lex_ctx_t, ir_value *frame, ir_value *think);
|
||||
|
||||
/* This is to create an instruction of the form
|
||||
* <outtype>%label := opcode a, b
|
||||
*/
|
||||
ir_instr* ir_block_create_phi(ir_block*, lex_ctx_t, const char *label, int vtype);
|
||||
ir_instr* ir_block_create_phi(ir_block*, lex_ctx_t, const char *label, qc_type vtype);
|
||||
ir_value* ir_phi_value(ir_instr*);
|
||||
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
|
||||
ir_instr* ir_block_create_call(ir_block*, lex_ctx_t, const char *label, ir_value *func, bool noreturn);
|
||||
|
@ -175,29 +184,35 @@ bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx_t, ir_block *to);
|
|||
|
||||
/* function */
|
||||
struct ir_function {
|
||||
char *name;
|
||||
int outtype;
|
||||
int *params;
|
||||
ir_block **blocks;
|
||||
ir_flag_t flags;
|
||||
int builtin;
|
||||
ir_function(ir_builder *owner, qc_type returntype);
|
||||
~ir_function();
|
||||
|
||||
ir_builder *m_owner;
|
||||
|
||||
std::string m_name;
|
||||
qc_type m_outtype;
|
||||
int *m_params = nullptr;
|
||||
ir_flag_t m_flags = 0;
|
||||
int m_builtin = 0;
|
||||
|
||||
std::vector<std::unique_ptr<ir_block>> m_blocks;
|
||||
|
||||
/*
|
||||
* values generated from operations
|
||||
* which might get optimized away, so anything
|
||||
* in there needs to be deleted in the dtor.
|
||||
*/
|
||||
ir_value **values;
|
||||
ir_value **locals; /* locally defined variables */
|
||||
ir_value *value;
|
||||
std::vector<std::unique_ptr<ir_value>> m_values;
|
||||
std::vector<std::unique_ptr<ir_value>> m_locals; /* locally defined variables */
|
||||
ir_value *m_value = nullptr;
|
||||
|
||||
size_t allocated_locals;
|
||||
size_t globaltemps;
|
||||
size_t m_allocated_locals = 0;
|
||||
size_t m_globaltemps = 0;
|
||||
|
||||
ir_block* first;
|
||||
ir_block* last;
|
||||
ir_block* m_first = nullptr;
|
||||
ir_block* m_last = nullptr;
|
||||
|
||||
lex_ctx_t context;
|
||||
lex_ctx_t m_context;
|
||||
|
||||
/*
|
||||
* for prototypes - first we generate all the
|
||||
|
@ -206,19 +221,17 @@ struct ir_function {
|
|||
*
|
||||
* remember the ID:
|
||||
*/
|
||||
qcint_t code_function_def;
|
||||
qcint_t m_code_function_def = -1;
|
||||
|
||||
/* for temp allocation */
|
||||
size_t run_id;
|
||||
|
||||
ir_builder *owner;
|
||||
size_t m_run_id = 0;
|
||||
|
||||
/* vararg support: */
|
||||
size_t max_varargs;
|
||||
size_t m_max_varargs = 0;
|
||||
};
|
||||
|
||||
|
||||
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
|
||||
ir_value* ir_function_create_local(ir_function *self, const std::string& name, qc_type vtype, bool param);
|
||||
bool GMQCC_WARN ir_function_finalize(ir_function*);
|
||||
ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char *label);
|
||||
|
||||
|
@ -227,47 +240,53 @@ ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char
|
|||
#define IR_MAX_VINSTR_TEMPS 1
|
||||
|
||||
struct ir_builder {
|
||||
char *name;
|
||||
ir_function **functions;
|
||||
ir_value **globals;
|
||||
ir_value **fields;
|
||||
ir_value **const_floats; /* for reusing them in vector-splits, TODO: sort this or use a radix-tree */
|
||||
ir_builder(const std::string& modulename);
|
||||
~ir_builder();
|
||||
|
||||
ht htfunctions;
|
||||
ht htglobals;
|
||||
ht htfields;
|
||||
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;
|
||||
|
||||
ir_value **extparams;
|
||||
ir_value **extparam_protos;
|
||||
ht m_htfunctions;
|
||||
ht m_htglobals;
|
||||
ht m_htfields;
|
||||
|
||||
/* the highest func->allocated_locals */
|
||||
size_t max_locals;
|
||||
size_t max_globaltemps;
|
||||
uint32_t first_common_local;
|
||||
uint32_t first_common_globaltemp;
|
||||
// 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;
|
||||
|
||||
const char **filenames;
|
||||
qcint_t *filestrings;
|
||||
/* we cache the #IMMEDIATE string here */
|
||||
qcint_t str_immediate;
|
||||
/* there should just be this one nil */
|
||||
ir_value *nil;
|
||||
ir_value *reserved_va_count;
|
||||
ir_value *coverage_func;
|
||||
/* some virtual instructions require temps, and their code is isolated
|
||||
* so that we don't need to keep track of their liveness.
|
||||
*/
|
||||
ir_value *vinstr_temp[IR_MAX_VINSTR_TEMPS];
|
||||
ir_value *m_vinstr_temp[IR_MAX_VINSTR_TEMPS];
|
||||
|
||||
/* code generator */
|
||||
code_t *code;
|
||||
std::unique_ptr<code_t> m_code;
|
||||
};
|
||||
|
||||
ir_builder* ir_builder_new(const char *modulename);
|
||||
void ir_builder_delete(ir_builder*);
|
||||
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
|
||||
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
|
||||
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
|
||||
ir_function* ir_builder_create_function(ir_builder*, const std::string& name, qc_type outtype);
|
||||
ir_value* ir_builder_create_global(ir_builder*, const std::string& name, qc_type vtype);
|
||||
ir_value* ir_builder_create_field(ir_builder*, const std::string& name, qc_type vtype);
|
||||
ir_value* ir_builder_get_va_count(ir_builder*);
|
||||
bool ir_builder_generate(ir_builder *self, const char *filename);
|
||||
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
||||
|
|
|
@ -65,7 +65,7 @@ static void lex_token_new(lex_file *lex)
|
|||
if (lex->tok.value)
|
||||
vec_shrinkto(lex->tok.value, 0);
|
||||
|
||||
lex->tok.constval.t = 0;
|
||||
lex->tok.constval.t = TYPE_VOID;
|
||||
lex->tok.ctx.line = lex->sline;
|
||||
lex->tok.ctx.file = lex->name;
|
||||
lex->tok.ctx.column = lex->column;
|
||||
|
|
2
lexer.h
2
lexer.h
|
@ -9,7 +9,7 @@ struct token {
|
|||
vec3_t v;
|
||||
int i;
|
||||
qcfloat_t f;
|
||||
int t; /* type */
|
||||
qc_type t; /* type */
|
||||
} constval;
|
||||
lex_ctx_t ctx;
|
||||
};
|
||||
|
|
1183
parser.cpp
1183
parser.cpp
File diff suppressed because it is too large
Load diff
26
test.cpp
26
test.cpp
|
@ -76,7 +76,7 @@ static FILE **task_popen(const char *command, const char *mode) {
|
|||
dup2(errhandle[1], 2);
|
||||
|
||||
execvp(argv[0], &argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
exit(95);
|
||||
} else {
|
||||
/* fork failed */
|
||||
goto task_popen_error_3;
|
||||
|
@ -98,11 +98,17 @@ static int task_pclose(FILE **handles) {
|
|||
close(data->pipes[1]); /* stdout */
|
||||
close(data->pipes[2]); /* stderr */
|
||||
|
||||
waitpid(data->pid, &status, 0);
|
||||
if (data->pid != waitpid(data->pid, &status, 0)) {
|
||||
abort();
|
||||
}
|
||||
if (!WIFEXITED(status))
|
||||
return -1;
|
||||
if (WIFSIGNALED(status))
|
||||
con_out("got signaled!\n");
|
||||
|
||||
mem_d(data);
|
||||
|
||||
return status;
|
||||
return status ? 1 : 0;
|
||||
}
|
||||
|
||||
#define TASK_COMPILE 0
|
||||
|
@ -997,6 +1003,10 @@ static size_t task_schedualize(size_t *pad) {
|
|||
util_snprintf(space[1], sizeof(space[1]), "%d", (int)(i));
|
||||
|
||||
con_out("test #%u %*s", i, strlen(space[0]) - strlen(space[1]), "");
|
||||
//con_out("[[%*s]]",
|
||||
// (pad[0] + pad[1] - strlen(it.tmpl->description)) + (strlen(it.tmpl->rulesfile) - pad[1]),
|
||||
// it.tmpl->rulesfile);
|
||||
//fflush(stdout);
|
||||
|
||||
/*
|
||||
* Generate a task from thin air if it requires execution in
|
||||
|
@ -1057,6 +1067,16 @@ static size_t task_schedualize(size_t *pad) {
|
|||
}
|
||||
|
||||
status = task_pclose(it.runhandles);
|
||||
if (status != 0 && status != 1) {
|
||||
con_out("compiler failure (returned: %i): `%s` %*s\n",
|
||||
status,
|
||||
it.tmpl->description,
|
||||
(pad[0] + pad[1] - strlen(it.tmpl->description)) + (strlen(it.tmpl->rulesfile) - pad[1]),
|
||||
it.tmpl->rulesfile
|
||||
);
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
if ((!strcmp(it.tmpl->proceduretype, "-fail") && status == EXIT_SUCCESS)
|
||||
|| ( strcmp(it.tmpl->proceduretype, "-fail") && status == EXIT_FAILURE)) {
|
||||
con_out("failure: `%s` %*s %*s\n",
|
||||
|
|
13
tests/fieldfuncs.qc
Normal file
13
tests/fieldfuncs.qc
Normal file
|
@ -0,0 +1,13 @@
|
|||
.float field;
|
||||
|
||||
.float getfield() {
|
||||
return field;
|
||||
}
|
||||
|
||||
void() main = {
|
||||
entity e = spawn();
|
||||
e.field = 42;
|
||||
print(ftos(e.(getfield())), "\n");
|
||||
.float memptr = getfield();
|
||||
print(ftos(e.memptr), "\n");
|
||||
}
|
6
tests/fieldfuncs.tmpl
Normal file
6
tests/fieldfuncs.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
I: fieldfuncs.qc
|
||||
D: test fields with functions
|
||||
T: -compile
|
||||
C: -std=fte
|
||||
M: 42
|
||||
M: 42
|
13
tests/forloop.qc
Normal file
13
tests/forloop.qc
Normal file
|
@ -0,0 +1,13 @@
|
|||
void main() {
|
||||
float j;
|
||||
for (j = 0; j < 2; ++j)
|
||||
print("+");
|
||||
|
||||
for (float i = 0; i < 5; ++i)
|
||||
print("*");
|
||||
|
||||
for (;;) {
|
||||
print("\n");
|
||||
break;
|
||||
}
|
||||
}
|
5
tests/forloop.tmpl
Normal file
5
tests/forloop.tmpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
I: forloop.qc
|
||||
D: test for loops
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: ++*****
|
Loading…
Reference in a new issue