mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-27 22:22:17 +00:00
Merge branch 'cooking'
This commit is contained in:
commit
1d745fd1f9
16 changed files with 306 additions and 310 deletions
5
ansi.c
5
ansi.c
|
@ -23,7 +23,6 @@
|
|||
#define GMQCC_PLATFORM_HEADER
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "gmqcc.h"
|
||||
|
@ -48,10 +47,6 @@ char *platform_strncat(char *dest, const char *src, size_t num) {
|
|||
return strncat(dest, src, num);
|
||||
}
|
||||
|
||||
const char *platform_tmpnam(char *str) {
|
||||
return tmpnam(str);
|
||||
}
|
||||
|
||||
const char *platform_getenv(const char *var) {
|
||||
return getenv(var);
|
||||
}
|
||||
|
|
13
ast.c
13
ast.c
|
@ -1207,9 +1207,6 @@ ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype
|
|||
self->fixedparams = NULL;
|
||||
self->return_value = NULL;
|
||||
|
||||
self->accumulate = NULL;
|
||||
self->accumulation = 0;
|
||||
|
||||
return self;
|
||||
|
||||
cleanup:
|
||||
|
@ -1867,16 +1864,6 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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))
|
||||
|
|
53
ast.h
53
ast.h
|
@ -25,6 +25,8 @@
|
|||
#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.
|
||||
*/
|
||||
|
@ -53,6 +55,28 @@ typedef struct ast_label_s ast_label;
|
|||
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 */
|
||||
|
@ -134,7 +158,7 @@ struct ast_expression_common
|
|||
/* 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
|
||||
*/
|
||||
|
@ -147,22 +171,6 @@ struct ast_expression_common
|
|||
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
|
||||
*
|
||||
|
@ -617,10 +625,6 @@ struct ast_function_s
|
|||
|
||||
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;
|
||||
|
@ -660,4 +664,11 @@ const char* ast_function_label(ast_function*, const char *prefix);
|
|||
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
|
||||
|
|
34
fold.c
34
fold.c
|
@ -773,18 +773,28 @@ ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **a
|
|||
#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;
|
||||
|
||||
|
@ -793,14 +803,16 @@ ast_expression *fold_superfluous(ast_expression *left, ast_expression *right, in
|
|||
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;
|
||||
|
||||
|
@ -808,7 +820,8 @@ ast_expression *fold_superfluous(ast_expression *left, ast_expression *right, in
|
|||
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;
|
||||
}
|
||||
|
@ -816,6 +829,13 @@ ast_expression *fold_superfluous(ast_expression *left, ast_expression *right, in
|
|||
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;
|
||||
|
|
81
ftepp.c
81
ftepp.c
|
@ -74,17 +74,13 @@ typedef struct ftepp_s {
|
|||
char *itemname;
|
||||
char *includename;
|
||||
bool in_macro;
|
||||
|
||||
uint32_t predef_countval;
|
||||
uint32_t predef_randval;
|
||||
} 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;
|
||||
|
||||
/* __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;
|
||||
|
@ -99,7 +95,7 @@ static char *ftepp_predef_date(lex_file *context) {
|
|||
}
|
||||
|
||||
/* __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;
|
||||
|
@ -114,61 +110,58 @@ static char *ftepp_predef_time(lex_file *context) {
|
|||
}
|
||||
|
||||
/* __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);
|
||||
|
@ -183,7 +176,7 @@ static char *ftepp_predef_timestamp(lex_file *context) {
|
|||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
char *(*func)(lex_file *);
|
||||
char *(*func)(ftepp_t *);
|
||||
} ftepp_predef_t;
|
||||
|
||||
static const ftepp_predef_t ftepp_predefs[] = {
|
||||
|
@ -213,7 +206,7 @@ bool ftepp_predef_exists(const char *name) {
|
|||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
@ -305,8 +298,10 @@ static ftepp_t* ftepp_new(void)
|
|||
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;
|
||||
}
|
||||
|
@ -1686,9 +1681,9 @@ static bool ftepp_preprocess(ftepp_t *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);
|
||||
|
||||
|
|
1
gmqcc.h
1
gmqcc.h
|
@ -315,7 +315,6 @@ const char *util_ctime (const time_t *timer);
|
|||
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
|
||||
|
|
4
ir.c
4
ir.c
|
@ -1586,10 +1586,6 @@ bool ir_block_create_return(ir_block *self, lex_ctx_t ctx, ir_value *v)
|
|||
|
||||
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)
|
||||
|
|
201
ir.h
201
ir.h
|
@ -24,69 +24,86 @@
|
|||
#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 {
|
||||
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;
|
||||
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,
|
||||
|
||||
struct ir_instr_s **reads;
|
||||
struct ir_instr_s **writes;
|
||||
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;
|
||||
|
||||
|
||||
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*);
|
||||
|
@ -100,19 +117,17 @@ bool ir_value_lives(ir_value*, size_t);
|
|||
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;
|
||||
|
@ -123,31 +138,30 @@ typedef struct ir_instr_s
|
|||
/* 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);
|
||||
|
@ -170,7 +184,8 @@ bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx_t, ir_value *opt_value
|
|||
|
||||
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,
|
||||
|
@ -180,37 +195,33 @@ bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx_t, ir_block *to);
|
|||
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
|
||||
*
|
||||
|
@ -221,30 +232,22 @@ typedef struct ir_function_s
|
|||
/* 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;
|
||||
|
@ -277,7 +280,7 @@ typedef struct ir_builder_s
|
|||
|
||||
/* code generator */
|
||||
code_t *code;
|
||||
} ir_builder;
|
||||
};
|
||||
|
||||
ir_builder* ir_builder_new(const char *modulename);
|
||||
void ir_builder_delete(ir_builder*);
|
||||
|
@ -293,7 +296,15 @@ void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
|||
* 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
|
||||
|
|
4
msvc.c
4
msvc.c
|
@ -76,10 +76,6 @@ char *platform_strncat(char *dest, const char *src, size_t num) {
|
|||
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;
|
||||
|
|
156
parser.c
156
parser.c
|
@ -44,7 +44,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
|
|||
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, ...)
|
||||
{
|
||||
|
@ -521,10 +521,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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",
|
||||
|
@ -546,10 +546,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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",
|
||||
|
@ -576,15 +576,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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",
|
||||
|
@ -604,7 +604,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
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));
|
||||
|
@ -657,7 +657,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
* 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 {
|
||||
|
@ -670,11 +670,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
* 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]);
|
||||
}
|
||||
|
@ -727,7 +727,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
}
|
||||
}
|
||||
out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
|
||||
out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -774,7 +774,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
|
||||
if (!(out = fold_op(parser->fold, op, exprs))) {
|
||||
out = (ast_expression*)ast_binary_new(
|
||||
out = fold_binary(
|
||||
parser_ctx(parser),
|
||||
VINSTR_CROSS,
|
||||
exprs[0],
|
||||
|
@ -795,6 +795,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
|
||||
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;
|
||||
|
@ -835,7 +836,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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) {
|
||||
|
@ -845,7 +846,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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) {
|
||||
|
@ -855,7 +856,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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('='):
|
||||
|
@ -969,9 +970,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
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('+','='):
|
||||
|
@ -1036,9 +1037,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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;
|
||||
|
@ -1095,9 +1096,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
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]);
|
||||
|
@ -1117,9 +1118,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
}
|
||||
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;
|
||||
|
@ -1422,7 +1423,7 @@ static ast_expression* parse_vararg_do(parser_t *parser)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
typevar = parse_typename(parser, NULL, NULL);
|
||||
typevar = parse_typename(parser, NULL, NULL, NULL);
|
||||
if (!typevar) {
|
||||
ast_unref(idx);
|
||||
return NULL;
|
||||
|
@ -3984,59 +3985,28 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -4064,13 +4034,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -4100,7 +4070,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
|
|||
|
||||
vec_push(func->blocks, block);
|
||||
|
||||
|
||||
parser->function = old;
|
||||
if (!parser_leaveblock(parser))
|
||||
retval = false;
|
||||
|
@ -4537,7 +4506,6 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
|
|||
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;
|
||||
|
@ -4563,6 +4531,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
|
|||
|
||||
/* 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 != ',') {
|
||||
|
@ -4576,10 +4546,13 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
@ -4590,13 +4563,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
|
|||
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++ */
|
||||
|
@ -4748,7 +4715,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
|
|||
* 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;
|
||||
|
@ -4758,6 +4725,8 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
|
|||
bool wasarray = false;
|
||||
size_t morefields = 0;
|
||||
|
||||
bool vararg = (parser->tok == TOKEN_DOTS);
|
||||
|
||||
ctx = parser_ctx(parser);
|
||||
|
||||
/* types may start with a dot */
|
||||
|
@ -4781,6 +4750,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
|
|||
morefields += 3;
|
||||
else
|
||||
break;
|
||||
vararg = false;
|
||||
if (!parser_next(parser)) {
|
||||
parseerror(parser, "expected typename for field definition");
|
||||
return NULL;
|
||||
|
@ -4790,6 +4760,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
|
|||
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;
|
||||
}
|
||||
|
@ -4910,7 +4884,7 @@ static bool parse_typedef(parser_t *parser)
|
|||
ast_value *typevar, *oldtype;
|
||||
ast_expression *old;
|
||||
|
||||
typevar = parse_typename(parser, NULL, NULL);
|
||||
typevar = parse_typename(parser, NULL, NULL, NULL);
|
||||
|
||||
if (!typevar)
|
||||
return false;
|
||||
|
@ -5080,7 +5054,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
|
|||
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);
|
||||
|
|
2
parser.h
2
parser.h
|
@ -134,7 +134,7 @@ bool fold_generate (fold_t *, ir_builder *);
|
|||
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 *);
|
||||
|
||||
|
|
18
platform.h
18
platform.h
|
@ -185,24 +185,6 @@ const char *platform_ctime(const time_t *timer);
|
|||
*/
|
||||
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.
|
||||
|
|
4
test.c
4
test.c
|
@ -163,8 +163,8 @@ static int task_pclose(fs_file_t **handles) {
|
|||
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 */
|
||||
|
||||
|
|
27
tests/dots.qc
Normal file
27
tests/dots.qc
Normal file
|
@ -0,0 +1,27 @@
|
|||
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);
|
||||
}
|
8
tests/dots.tmpl
Normal file
8
tests/dots.tmpl
Normal file
|
@ -0,0 +1,8 @@
|
|||
I: dots.qc
|
||||
D: TOKEN_DOTS disambiguation
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 123
|
||||
M: 123
|
||||
M: and: 123
|
||||
M: and: 123
|
5
util.c
5
util.c
|
@ -281,11 +281,6 @@ bool util_isatty(fs_file_t *file) {
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue