#define GMQCC_PLATFORM_HEADER
#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
#include "platform.h"
#include "gmqcc.h"
return strncat(dest, src, num);
}
-const char *platform_tmpnam(char *str) {
- return tmpnam(str);
-}
-
const char *platform_getenv(const char *var) {
return getenv(var);
}
self->fixedparams = NULL;
self->return_value = NULL;
- self->accumulate = NULL;
- self->accumulation = 0;
-
return self;
cleanup:
}
}
- /* generate the call for any accumulation */
- if (self->accumulate) {
- ast_call *call = ast_call_new(ast_ctx(self), (ast_expression*)self->accumulate->vtype);
- for (i = 0; i < vec_size(ec->params); i++)
- vec_push(call->params, (ast_expression*)ec->params[i]);
- vec_push(vec_last(self->blocks)->exprs, (ast_expression*)call);
-
- self->ir_func->flags |= IR_FLAG_ACCUMULATE;
- }
-
for (i = 0; i < vec_size(self->blocks); ++i) {
cgen = self->blocks[i]->expression.codegen;
if (!(*cgen)((ast_expression*)self->blocks[i], self, false, &dummy))
#define GMQCC_AST_HDR
#include "ir.h"
+typedef uint16_t ast_flag_t;
+
/* Note: I will not be using a _t suffix for the
* "main" ast node types for now.
*/
typedef struct ast_goto_s ast_goto;
typedef struct ast_argpipe_s ast_argpipe;
+enum {
+ AST_FLAG_VARIADIC = 1 << 0,
+ AST_FLAG_NORETURN = 1 << 1,
+ AST_FLAG_INLINE = 1 << 2,
+ AST_FLAG_INITIALIZED = 1 << 3,
+ AST_FLAG_DEPRECATED = 1 << 4,
+ AST_FLAG_INCLUDE_DEF = 1 << 5,
+ AST_FLAG_IS_VARARG = 1 << 6,
+ AST_FLAG_ALIAS = 1 << 7,
+ AST_FLAG_ERASEABLE = 1 << 8,
+ AST_FLAG_ACCUMULATE = 1 << 9,
+
+ /*
+ * An array declared as []
+ * so that the size is taken from the initializer
+ */
+ AST_FLAG_ARRAY_INIT = 1 << 10,
+
+ AST_FLAG_LAST,
+ AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
+};
+
enum {
TYPE_ast_node, /* 0 */
TYPE_ast_expression, /* 1 */
/* arrays get a member-count */
size_t count;
ast_value* *params;
- uint32_t flags;
+ ast_flag_t flags;
/* void foo(string...) gets varparam set as a restriction
* for variadic parameters
*/
ir_value *outl;
ir_value *outr;
};
-#define AST_FLAG_VARIADIC (1<<0)
-#define AST_FLAG_NORETURN (1<<1)
-#define AST_FLAG_INLINE (1<<2)
-#define AST_FLAG_INITIALIZED (1<<3)
-#define AST_FLAG_DEPRECATED (1<<4)
-#define AST_FLAG_INCLUDE_DEF (1<<5)
-#define AST_FLAG_IS_VARARG (1<<6)
-#define AST_FLAG_ALIAS (1<<7)
-#define AST_FLAG_ERASEABLE (1<<8)
-#define AST_FLAG_ACCUMULATE (1<<9)
-/*
- * An array declared as []
- * so that the size is taken from the initializer
- */
-#define AST_FLAG_ARRAY_INIT (1<<10)
-#define AST_FLAG_TYPE_MASK (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
/* Value
*
int builtin;
- /* function accumulation */
- ast_function *accumulate; /* pointer to the next function in the chain */
- size_t accumulation; /* base functions # of accumulations */
-
ir_function *ir_func;
ir_block *curblock;
ir_block **breakblocks;
bool ast_function_codegen(ast_function *self, ir_builder *builder);
bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir);
+/*
+ * If the condition creates a situation where this becomes -1 size it means there are
+ * more AST_FLAGs than the type ast_flag_t is capable of holding. So either eliminate
+ * the AST flag count or change the ast_flag_t typedef to a type large enough to accomodate
+ * all the flags.
+ */
+typedef int static_assert_is_ast_flag_safe [((AST_FLAG_LAST) <= (ast_flag_t)(-1)) ? 1 : -1];
#endif
#define fold_can_1(X) ((X)->hasvalue && (X)->cvq == CV_CONST)
/*#define fold_can_2(X,Y) (fold_can_1(X) && fold_can_1(Y))*/
-ast_expression *fold_superfluous(ast_expression *left, ast_expression *right, int op) {
+static ast_expression *fold_superfluous(ast_expression *left, ast_expression *right, int op) {
+ ast_expression *swapped = NULL; /* using this as bool */
ast_value *load;
- if (!ast_istype(left, ast_value) || !fold_can_1((load = (ast_value*)right)))
+ if (!ast_istype(right, ast_value) || !fold_can_1((load = (ast_value*)right))) {
+ swapped = left;
+ left = right;
+ right = swapped;
+ }
+
+ if (!ast_istype(right, ast_value) || !fold_can_1((load = (ast_value*)right)))
return NULL;
switch (op) {
- case INSTR_MUL_F:
case INSTR_DIV_F:
+ if (swapped)
+ return NULL;
+ case INSTR_MUL_F:
if (fold_immvalue_float(load) == 1.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
- return (ast_expression*)left;
+ ast_unref(right);
+ return left;
}
break;
case INSTR_SUB_F:
if (fold_immvalue_float(load) == 0.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
- return (ast_expression*)left;
+ ast_unref(right);
+ return left;
}
break;
case INSTR_MUL_V:
if (vec3_cmp(fold_immvalue_vector(load), vec3_create(1, 1, 1))) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
- return (ast_expression*)left;
+ ast_unref(right);
+ return left;
}
break;
case INSTR_SUB_V:
if (vec3_cmp(fold_immvalue_vector(load), vec3_create(0, 0, 0))) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
- return (ast_expression*)left;
+ ast_unref(right);
+ return left;
}
break;
}
return NULL;
}
+ast_expression *fold_binary(lex_ctx_t ctx, int op, ast_expression *left, ast_expression *right) {
+ ast_expression *ret = fold_superfluous(left, right, op);
+ if (ret)
+ return ret;
+ return (ast_expression*)ast_binary_new(ctx, op, left, right);
+}
+
static GMQCC_INLINE int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
if (isfloat(condval) && fold_can_1(condval) && OPTS_OPTIMIZATION(OPTIM_CONST_FOLD_DCE)) {
ast_expression_codegen *cgen;
char *itemname;
char *includename;
bool in_macro;
-} ftepp_t;
-/*
- * Implement the predef subsystem now. We can do this safely with the
- * help of lexer contexts.
- */
-static uint32_t ftepp_predef_countval = 0;
-static uint32_t ftepp_predef_randval = 0;
+ uint32_t predef_countval;
+ uint32_t predef_randval;
+} ftepp_t;
/* __DATE__ */
-static char *ftepp_predef_date(lex_file *context) {
+static char *ftepp_predef_date(ftepp_t *context) {
const struct tm *itime = NULL;
char *value = (char*)mem_a(82);
time_t rtime;
}
/* __TIME__ */
-static char *ftepp_predef_time(lex_file *context) {
+static char *ftepp_predef_time(ftepp_t *context) {
const struct tm *itime = NULL;
char *value = (char*)mem_a(82);
time_t rtime;
}
/* __LINE__ */
-static char *ftepp_predef_line(lex_file *context) {
- char *value;
- util_asprintf(&value, "%d", (int)context->line);
+static char *ftepp_predef_line(ftepp_t *context) {
+ char *value;
+
+ util_asprintf(&value, "%d", (int)context->lex->line);
return value;
}
/* __FILE__ */
-static char *ftepp_predef_file(lex_file *context) {
- size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
- char *value = (char*)mem_a(length);
- util_snprintf(value, length, "\"%s\"", context->name);
+static char *ftepp_predef_file(ftepp_t *context) {
+ size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */
+ char *value = (char*)mem_a(length);
+ util_snprintf(value, length, "\"%s\"", context->lex->name);
return value;
}
/* __COUNTER_LAST__ */
-static char *ftepp_predef_counterlast(lex_file *context) {
- char *value;
- util_asprintf(&value, "%u", ftepp_predef_countval);
-
- (void)context;
+static char *ftepp_predef_counterlast(ftepp_t *context) {
+ char *value;
+ util_asprintf(&value, "%u", context->predef_countval);
return value;
}
/* __COUNTER__ */
-static char *ftepp_predef_counter(lex_file *context) {
- char *value;
- ftepp_predef_countval ++;
- util_asprintf(&value, "%u", ftepp_predef_countval);
- (void)context;
+static char *ftepp_predef_counter(ftepp_t *context) {
+ char *value;
+
+ context->predef_countval ++;
+ util_asprintf(&value, "%u", context->predef_countval);
return value;
}
/* __RANDOM__ */
-static char *ftepp_predef_random(lex_file *context) {
- char *value;
- ftepp_predef_randval = (util_rand() % 0xFF) + 1;
- util_asprintf(&value, "%u", ftepp_predef_randval);
+static char *ftepp_predef_random(ftepp_t *context) {
+ char *value;
- (void)context;
+ context->predef_randval = (util_rand() % 0xFF) + 1;
+ util_asprintf(&value, "%u", context->predef_randval);
return value;
}
/* __RANDOM_LAST__ */
-static char *ftepp_predef_randomlast(lex_file *context) {
- char *value;
- util_asprintf(&value, "%u", ftepp_predef_randval);
+static char *ftepp_predef_randomlast(ftepp_t *context) {
+ char *value;
- (void)context;
+ util_asprintf(&value, "%u", context->predef_randval);
return value;
}
/* __TIMESTAMP__ */
-static char *ftepp_predef_timestamp(lex_file *context) {
+static char *ftepp_predef_timestamp(ftepp_t *context) {
struct stat finfo;
const char *find;
char *value;
size_t size;
- if (stat(context->name, &finfo))
+ if (stat(context->lex->name, &finfo))
return util_strdup("\"<failed to determine timestamp>\"");
find = util_ctime(&finfo.st_mtime);
typedef struct {
const char *name;
- char *(*func)(lex_file *);
+ char *(*func)(ftepp_t *);
} ftepp_predef_t;
static const ftepp_predef_t ftepp_predefs[] = {
}
/* singleton because we're allowed */
-static GMQCC_INLINE char *(*ftepp_predef(const char *name))(lex_file *context) {
+static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) {
size_t i = ftepp_predef_index(name);
return (i != 0) ? ftepp_predefs[i-1].func : NULL;
}
ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
memset(ftepp, 0, sizeof(*ftepp));
- ftepp->macros = util_htnew(HT_MACROS);
- ftepp->output_on = true;
+ ftepp->macros = util_htnew(HT_MACROS);
+ ftepp->output_on = true;
+ ftepp->predef_countval = 0;
+ ftepp->predef_randval = 0;
return ftepp;
}
case TOKEN_TYPENAME:
/* is it a predef? */
if (OPTS_FLAG(FTEPP_PREDEFS)) {
- char *(*predef)(lex_file*) = ftepp_predef(ftepp_tokval(ftepp));
+ char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
if (predef) {
- expand = predef(ftepp->lex);
+ expand = predef(ftepp);
ftepp_out (ftepp, expand, false);
ftepp_next(ftepp);
typedef struct fs_file_s fs_file_t;
bool util_isatty(fs_file_t *);
-const char *util_tmpnam(char *);
/*
* A flexible vector implementation: all vector pointers contain some
self->final = true;
- /* can eliminate the return instructions for accumulation */
- if (self->owner->flags & IR_FLAG_ACCUMULATE)
- return true;
-
self->is_return = true;
in = ir_instr_new(ctx, self, INSTR_RETURN);
if (!in)
#define GMQCC_IR_HDR
#include "gmqcc.h"
-typedef struct
-{
+/*
+ * Type large enough to hold all the possible IR flags. This should be
+ * changed if the static assertion at the end of this file fails.
+ */
+typedef uint8_t ir_flag_t;
+
+typedef struct ir_value_s ir_value;
+typedef struct ir_instr_s ir_instr;
+typedef struct ir_block_s ir_block;
+typedef struct ir_function_s ir_function;
+typedef struct ir_builder_s ir_builder;
+
+typedef struct {
/* both inclusive */
size_t start;
size_t end;
} ir_life_entry_t;
-struct ir_function_s;
-typedef struct ir_value_s {
+enum {
+ IR_FLAG_HAS_ARRAYS = 1 << 0,
+ IR_FLAG_HAS_UNINITIALIZED = 1 << 1,
+ IR_FLAG_HAS_GOTO = 1 << 2,
+ IR_FLAG_INCLUDE_DEF = 1 << 3,
+ IR_FLAG_ERASEABLE = 1 << 4,
+
+ IR_FLAG_LAST,
+ IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
+ IR_FLAG_MASK_NO_LOCAL_TEMPS = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
+};
+
+struct ir_value_s {
char *name;
- int vtype;
- int store;
- lex_ctx_t context;
- /* even the IR knows the subtype of a field */
- int fieldtype;
- /* and the output type of a function */
- int outtype;
- /* 'const' vs 'var' qualifier */
- int cvq;
- uint32_t flags;
-
- struct ir_instr_s **reads;
- struct ir_instr_s **writes;
+ 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_instr **reads;
+ ir_instr **writes;
/* constantvalues */
bool hasvalue;
union {
- qcfloat_t vfloat;
- int vint;
- vec3_t vvec;
- int32_t ivec[3];
- char *vstring;
- struct ir_value_s *vpointer;
- struct ir_function_s *vfunc;
+ qcfloat_t vfloat;
+ int vint;
+ vec3_t vvec;
+ int32_t ivec[3];
+ char *vstring;
+ ir_value *vpointer;
+ ir_function *vfunc;
} constval;
struct {
int32_t globaladdr;
int32_t name;
- /* filled by the local-allocator */
- int32_t local;
- /* added for members */
- int32_t addroffset;
- /* to generate field-addresses early */
- int32_t fieldaddr;
+ int32_t local; /* filled by the local-allocator */
+ int32_t addroffset; /* added for members */
+ int32_t fieldaddr; /* to generate field-addresses early */
} code;
/* for acessing vectors */
- struct ir_value_s *members[3];
- struct ir_value_s *memberof;
+ ir_value *members[3];
+ ir_value *memberof;
- /* arrays will never overlap with temps */
- bool unique_life;
- /* temps living during a CALL must be locked */
- bool locked;
- bool callparam;
- /* For the temp allocator */
- ir_life_entry_t *life;
-} ir_value;
+ bool unique_life; /* arrays will never overlap with temps */
+ bool locked; /* temps living during a CALL must be locked */
+ bool callparam;
-/* ir_value can be a variable, or created by an operation */
-/* if a result of an operation: the function should store
+ ir_life_entry_t *life; /* For the temp allocator */
+};
+
+/*
+ * ir_value can be a variable, or created by an operation
+ * 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*);
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
/* PHI data */
-typedef struct ir_phi_entry_s
-{
- ir_value *value;
- struct ir_block_s *from;
+typedef struct ir_phi_entry_s {
+ ir_value *value;
+ ir_block *from;
} ir_phi_entry_t;
/* instruction */
-typedef struct ir_instr_s
-{
- int opcode;
- lex_ctx_t context;
+struct ir_instr_s {
+ int opcode;
+ lex_ctx_t context;
ir_value* (_ops[3]);
- struct ir_block_s* (bops[2]);
+ ir_block* (bops[2]);
ir_phi_entry_t *phi;
ir_value **params;
/* For IFs */
bool likely;
- struct ir_block_s *owner;
-} ir_instr;
+ ir_block *owner;
+};
/* block */
-typedef struct ir_block_s
-{
+struct ir_block_s {
char *label;
- lex_ctx_t context;
+ lex_ctx_t context;
bool final; /* once a jump is added we're done */
- ir_instr **instr;
- struct ir_block_s **entries;
- struct ir_block_s **exits;
- ir_value **living;
+ ir_instr **instr;
+ ir_block **entries;
+ ir_block **exits;
+ ir_value **living;
/* For the temp-allocation */
size_t entry_id;
size_t eid;
bool is_return;
- struct ir_function_s *owner;
+ ir_function *owner;
bool generated;
size_t code_start;
-} ir_block;
+};
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_if(ir_block*, lex_ctx_t, ir_value *cond,
ir_block *ontrue, ir_block *onfalse);
-/* A 'goto' is an actual 'goto' coded in QC, whereas
+/*
+ * A 'goto' is an actual 'goto' coded in QC, whereas
* a 'jump' is a virtual construct which simply names the
* next block to go to.
* A goto usually becomes an OP_GOTO in the resulting code,
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx_t, ir_block *to);
/* function */
-typedef struct ir_function_s
-{
+struct ir_function_s {
char *name;
int outtype;
int *params;
ir_block **blocks;
+ ir_flag_t flags;
+ int builtin;
- uint32_t flags;
-
- int builtin;
-
- ir_value *value;
-
- /* values generated from operations
+ /*
+ * values generated from operations
* which might get optimized away, so anything
* in there needs to be deleted in the dtor.
*/
ir_value **values;
-
- /* locally defined variables */
- ir_value **locals;
+ ir_value **locals; /* locally defined variables */
+ ir_value *value;
size_t allocated_locals;
size_t globaltemps;
- ir_block* first;
- ir_block* last;
+ ir_block* first;
+ ir_block* last;
- lex_ctx_t context;
+ lex_ctx_t context;
- /* for prototypes - first we generate all the
+ /*
+ * for prototypes - first we generate all the
* globals, and we remember teh function-defs
* so we can later fill in the entry pos
*
/* for temp allocation */
size_t run_id;
- struct ir_builder_s *owner;
+ ir_builder *owner;
/* vararg support: */
size_t max_varargs;
-} ir_function;
+};
-#define IR_FLAG_HAS_ARRAYS (1<<1)
-#define IR_FLAG_HAS_UNINITIALIZED (1<<2)
-#define IR_FLAG_HAS_GOTO (1<<3)
-#define IR_FLAG_INCLUDE_DEF (1<<4)
-#define IR_FLAG_ERASEABLE (1<<5)
-#define IR_FLAG_ACCUMULATE (1<<6)
-#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
-#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
ir_value* ir_function_create_local(ir_function *self, const char *name, int 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);
/* builder */
-#define IR_HT_SIZE 1024
+#define IR_HT_SIZE 1024
#define IR_MAX_VINSTR_TEMPS 1
-typedef struct ir_builder_s
-{
+
+struct ir_builder_s {
char *name;
ir_function **functions;
ir_value **globals;
/* code generator */
code_t *code;
-} ir_builder;
+};
ir_builder* ir_builder_new(const char *modulename);
void ir_builder_delete(ir_builder*);
* Blub: don't use extern here, it's annoying and shows up in nm
* for some reason :P
*/
-typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4)?1:-1];
-typedef int static_assert_is_32bit_integer[(sizeof(qcfloat_t) == 4)?1:-1];
+typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4) ? 1 : -1];
+typedef int static_assert_is_32bit_integer[(sizeof(qcfloat_t) == 4) ? 1 : -1];
+
+/*
+ * If the condition creates a situation where this becomes -1 size it means there are
+ * more IR_FLAGs than the type ir_flag_t is capable of holding. So either eliminate
+ * the IR flag count or change the ir_flag_t typedef to a type large enough to accomodate
+ * all the flags.
+ */
+typedef int static_assert_is_ir_flag_safe [((IR_FLAG_LAST) <= (ir_flag_t)(-1)) ? 1 : -1];
#endif
return strncat_s(dest, num, src, _TRUNCATE);
}
-const char *platform_tmpnam(char *str) {
- return tmpnam_s(str, L_tmpnam);
-}
-
const char *platform_getenv(const char *var) {
char *buffer = (char *)platform_mem_allocate(GETENV_BUFFER);
size_t size;
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg);
static void parseerror(parser_t *parser, const char *fmt, ...)
{
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (exprs[1]->vtype == TYPE_VECTOR)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
if (exprs[1]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
}
if (!(out = fold_op(parser->fold, op, exprs))) {
if (exprs[0]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
else {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
* since scalar ^ vector is not allowed.
*/
if (exprs[0]->vtype == TYPE_FLOAT) {
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
exprs[0], exprs[1]);
} else {
* Bitop all the values of the vector components against the
* vectors components in question.
*/
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
exprs[0], exprs[1]);
} else {
- out = (ast_expression*)ast_binary_new(ctx,
+ out = fold_binary(ctx,
(op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
exprs[0], exprs[1]);
}
}
}
}
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
}
break;
}
if (!(out = fold_op(parser->fold, op, exprs))) {
- out = (ast_expression*)ast_binary_new(
+ out = fold_binary(
parser_ctx(parser),
VINSTR_CROSS,
exprs[0],
}
if (!(out = fold_op(parser->fold, op, exprs))) {
+ /* This whole block is NOT fold_binary safe */
ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
eq->refs = AST_REF_NONE;
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
break;
case opid2('!', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid2('=', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid1('='):
}
if (!out)
return false;
- out = (ast_expression*)ast_binary_new(ctx, subop,
- out,
- (ast_expression*)parser->fold->imm_float[1]);
+ out = fold_binary(ctx, subop,
+ out,
+ (ast_expression*)parser->fold->imm_float[1]);
break;
case opid2('+','='):
out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF,
exprs[0], exprs[1]);
} else {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
- (ast_expression*)parser->fold->imm_float[1],
- exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F,
+ (ast_expression*)parser->fold->imm_float[1],
+ exprs[1]);
if (!out) {
compile_error(ctx, "internal error: failed to generate division");
return false;
else
assignop = type_store_instr[exprs[0]->vtype];
if (exprs[0]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
if (!out)
return false;
(void)check_write_to(ctx, exprs[0]);
}
if (!(out = fold_op(parser->fold, op, exprs))) {
if (exprs[0]->vtype == TYPE_FLOAT) {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+ out = fold_binary(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
} else {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+ out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
}
}
break;
return NULL;
}
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar) {
ast_unref(idx);
return NULL;
}
}
- if (var->hasvalue && !(var->expression.flags & AST_FLAG_ACCUMULATE)) {
- parseerror(parser, "function `%s` declared with multiple bodies", var->name);
- ast_block_delete(block);
- goto enderr;
- }
-
- /* accumulation? */
- if (var->hasvalue && var->expression.vtype == TYPE_FUNCTION) {
- ast_value *accum = NULL;
- ast_function *previous = NULL;
- char acname[1024];
-
- /* only void please */
- if (var->expression.next->vtype != TYPE_VOID) {
- parseerror(parser, "accumulated function `%s` declared with return type `%s` (accumulated functions must return void)",
- var->name,
- type_name[var->expression.next->vtype]
- );
+ if (var->hasvalue) {
+ if (!(var->expression.flags & AST_FLAG_ACCUMULATE)) {
+ parseerror(parser, "function `%s` declared with multiple bodies", var->name);
ast_block_delete(block);
goto enderr;
}
+ func = var->constval.vfunc;
- /* generate a new name increasing the accumulation count*/
- util_snprintf(acname, sizeof(acname), "##ACCUMULATE_%s_%d", var->name, var->constval.vfunc->accumulation++);
- accum = ast_value_new(parser_ctx(parser), acname, ((ast_expression*)var)->vtype);
- if (!accum)
- return false;
-
- ast_type_adopt(accum, var);
- func = ast_function_new(ast_ctx(var), NULL, accum);
- if (!func)
- return false;
-
- parser_addglobal(parser, acname, (ast_expression*)accum);
- vec_push(parser->functions, func);
-
- /* update the previous calls accumulate pointer for the codegen */
- previous = var->constval.vfunc;
- while (previous->accumulate)
- previous = previous->accumulate;
-
- if (ast_istype(previous, ast_function))
- previous->accumulate = func;
-
+ if (!func) {
+ parseerror(parser, "internal error: NULL function: `%s`", var->name);
+ ast_block_delete(block);
+ goto enderr;
+ }
} else {
func = ast_function_new(ast_ctx(var), var->name, var);
- vec_push(parser->functions, func);
- }
- if (!func) {
- parseerror(parser, "failed to allocate function for `%s`", var->name);
- ast_block_delete(block);
- goto enderr;
+ if (!func) {
+ parseerror(parser, "failed to allocate function for `%s`", var->name);
+ ast_block_delete(block);
+ goto enderr;
+ }
+ vec_push(parser->functions, func);
}
parser_enterblock(parser);
}
}
- if (var->argcounter) {
+ if (var->argcounter && !func->argc) {
ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
parser_addlocal(parser, argc->name, (ast_expression*)argc);
func->argc = argc;
}
- if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
+ if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC && !func->varargs) {
char name[1024];
ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY);
varargs->expression.flags |= AST_FLAG_IS_VARARG;
vec_push(func->blocks, block);
-
parser->function = old;
if (!parser_leaveblock(parser))
retval = false;
return parser_create_array_getter_impl(parser, array);
}
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
{
lex_ctx_t ctx;
/* parse variables until we hit a closing paren */
while (parser->tok != ')') {
+ bool is_varargs = false;
+
if (!first) {
/* there must be commas between them */
if (parser->tok != ',') {
}
first = false;
- if (parser->tok == TOKEN_DOTS) {
+ param = parse_typename(parser, NULL, NULL, &is_varargs);
+ if (!param && !is_varargs)
+ goto on_error;
+ if (is_varargs) {
/* '...' indicates a varargs function */
variadic = true;
- if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+ if (parser->tok != ')' && parser->tok != TOKEN_IDENT) {
parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
goto on_error;
}
goto on_error;
}
}
- }
- else
- {
- /* for anything else just parse a typename */
- param = parse_typename(parser, NULL, NULL);
- if (!param)
- goto on_error;
+ } else {
vec_push(params, param);
if (param->expression.vtype >= TYPE_VARIANT) {
char tname[1024]; /* typename is reserved in C++ */
* void() foo(), bar
* then the type-information 'void()' can be stored in 'storebase'
*/
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg)
{
ast_value *var, *tmp;
lex_ctx_t ctx;
bool wasarray = false;
size_t morefields = 0;
+ bool vararg = (parser->tok == TOKEN_DOTS);
+
ctx = parser_ctx(parser);
/* types may start with a dot */
morefields += 3;
else
break;
+ vararg = false;
if (!parser_next(parser)) {
parseerror(parser, "expected typename for field definition");
return NULL;
if (parser->tok == TOKEN_IDENT)
cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0);
if (!cached_typedef && parser->tok != TOKEN_TYPENAME) {
+ if (vararg && is_vararg) {
+ *is_vararg = true;
+ return NULL;
+ }
parseerror(parser, "expected typename");
return NULL;
}
ast_value *typevar, *oldtype;
ast_expression *old;
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar)
return false;
parseerror(parser, "`static` qualifier is not supported in global scope");
/* get the first complete variable */
- var = parse_typename(parser, &basetype, cached_typedef);
+ var = parse_typename(parser, &basetype, cached_typedef, NULL);
if (!var) {
if (basetype)
ast_delete(basetype);
ast_expression *fold_op (fold_t *, const oper_info *, ast_expression **);
ast_expression *fold_intrin (fold_t *, const char *, ast_expression **);
-ast_expression *fold_superfluous (ast_expression *, ast_expression *, int);
+ast_expression *fold_binary (lex_ctx_t ctx, int, ast_expression *, ast_expression *);
int fold_cond_ifthen (ir_value *, ast_function *, ast_ifthen *);
int fold_cond_ternary (ir_value *, ast_function *, ast_ternary *);
*/
char *platform_strncat(char *dest, const char *src, size_t num);
-/*
- * Function: platform_tmpnam
- * Generates names you can use to create temporary files.
- *
- * Parameters:
- * str - Pointer that will hold the generated name and will be identical
- * to the name returned by the function. This is a convenient way
- * to save the generated name.
- *
- * Returns:
- * Pointer to the name generate or *NULL* if there is a failure. Failure
- * can occur if you attempt more than TMP_MAX calls.
- *
- * Remarks:
- * Returns a name unique in the current workign directory.
- */
-const char *platform_tmpnam(char *str);
-
/*
* Function: platform_getenv
* Get a value from the current enviroment.
char *cmd = NULL;
popen_t *open = (popen_t*)mem_a(sizeof(popen_t));
- util_tmpnam(open->name_err);
- util_tmpnam(open->name_out);
+ tmpnam(open->name_err);
+ tmpnam(open->name_out);
(void)mode; /* excluded */
--- /dev/null
+entity self;
+.float f;
+..float fp;
+...float fpp;
+
+void try(entity e, ...float pp) {
+ print("and: ", ftos( e.(e.(e.pp)) ), "\n");
+}
+
+typedef float Float;
+
+void try2(entity e, ...Float pp) {
+ print("and: ", ftos( e.(e.(e.pp)) ), "\n");
+}
+
+// whereas the varargs are tested in vararg tests
+
+void main() {
+ self = spawn();
+ self.f = 123;
+ self.fp = f;
+ self.fpp = fp;
+ print(ftos( self.(self.fp) ), "\n");
+ print(ftos( self.(self.(self.fpp)) ), "\n");
+ try(self, fpp);
+ try2(self, fpp);
+}
--- /dev/null
+I: dots.qc
+D: TOKEN_DOTS disambiguation
+T: -execute
+C: -std=fteqcc
+M: 123
+M: 123
+M: and: 123
+M: and: 123
if (file == (fs_file_t*)stderr) return !!platform_isatty(STDERR_FILENO);
return false;
}
-
-const char *util_tmpnam(char *str) {
- return platform_tmpnam(str);
-}
-
/*
* A small noncryptographic PRNG based on:
* http://burtleburtle.net/bob/rand/smallprng.html