Merge branch 'cleanup' of github.com:graphitemaster/gmqcc into cleanup

This commit is contained in:
Dale Weiler 2015-01-30 00:20:41 -05:00
commit 90b5a6538a
18 changed files with 3639 additions and 3960 deletions

18
algo.h Normal file
View 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

2434
ast.cpp

File diff suppressed because it is too large Load diff

440
ast.h
View file

@ -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 */

View file

@ -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
View file

@ -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
View file

@ -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 */

File diff suppressed because it is too large Load diff

View file

@ -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();

2707
ir.cpp

File diff suppressed because it is too large Load diff

221
ir.h
View file

@ -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*, ...));

View file

@ -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;

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
I: forloop.qc
D: test for loops
T: -execute
C: -std=gmqcc
M: ++*****