mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-03-22 10:41:43 +00:00
Merge branch 'master' into blub/parser
This commit is contained in:
commit
82d9651529
2 changed files with 201 additions and 6 deletions
175
ast.c
175
ast.c
|
@ -58,6 +58,8 @@ static void ast_expression_init(ast_expression *self,
|
|||
self->expression.codegen = codegen;
|
||||
self->expression.vtype = TYPE_VOID;
|
||||
self->expression.next = NULL;
|
||||
self->expression.outl = NULL;
|
||||
self->expression.outr = NULL;
|
||||
MEM_VECTOR_INIT(&self->expression, params);
|
||||
}
|
||||
|
||||
|
@ -234,6 +236,39 @@ void ast_binary_delete(ast_binary *self)
|
|||
mem_d(self);
|
||||
}
|
||||
|
||||
ast_binstore* ast_binstore_new(lex_ctx ctx, int storop, int op,
|
||||
ast_expression* left, ast_expression* right)
|
||||
{
|
||||
ast_instantiate(ast_binstore, ctx, ast_binstore_delete);
|
||||
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binstore_codegen);
|
||||
|
||||
self->opstore = storop;
|
||||
self->opbin = op;
|
||||
self->dest = left;
|
||||
self->source = right;
|
||||
|
||||
self->expression.vtype = left->expression.vtype;
|
||||
if (left->expression.next) {
|
||||
self->expression.next = ast_type_copy(ctx, left);
|
||||
if (!self->expression.next) {
|
||||
ast_delete(self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
self->expression.next = NULL;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void ast_binstore_delete(ast_binstore *self)
|
||||
{
|
||||
ast_unref(self->dest);
|
||||
ast_unref(self->source);
|
||||
ast_expression_delete((ast_expression*)self);
|
||||
mem_d(self);
|
||||
}
|
||||
|
||||
ast_unary* ast_unary_new(lex_ctx ctx, int op,
|
||||
ast_expression *expr)
|
||||
{
|
||||
|
@ -823,6 +858,10 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
|
|||
* of the form: (a, b, c) = x should not assign to c...
|
||||
*/
|
||||
(void)lvalue;
|
||||
if (self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* output is NULL at first, we'll have each expression
|
||||
* assign to out output, thus, a comma-operator represention
|
||||
|
@ -846,6 +885,8 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
|
|||
return false;
|
||||
}
|
||||
|
||||
self->expression.outr = *out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -854,10 +895,21 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
|
|||
ast_expression_codegen *cgen;
|
||||
ir_value *left, *right;
|
||||
|
||||
if (lvalue && self->expression.outl) {
|
||||
*out = self->expression.outl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lvalue && self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->dest->expression.codegen;
|
||||
/* lvalue! */
|
||||
if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
|
||||
return false;
|
||||
self->expression.outl = left;
|
||||
|
||||
cgen = self->source->expression.codegen;
|
||||
/* rvalue! */
|
||||
|
@ -866,6 +918,7 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
|
|||
|
||||
if (!ir_block_create_store_op(func->curblock, self->op, left, right))
|
||||
return false;
|
||||
self->expression.outr = right;
|
||||
|
||||
/* Theoretically, an assinment returns its left side as an
|
||||
* lvalue, if we don't need an lvalue though, we return
|
||||
|
@ -887,7 +940,11 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
|
|||
/* In the context of a binary operation, we can disregard
|
||||
* the lvalue flag.
|
||||
*/
|
||||
(void)lvalue;
|
||||
(void)lvalue;
|
||||
if (self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->left->expression.codegen;
|
||||
/* lvalue! */
|
||||
|
@ -903,6 +960,61 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
|
|||
self->op, left, right);
|
||||
if (!*out)
|
||||
return false;
|
||||
self->expression.outr = *out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, ir_value **out)
|
||||
{
|
||||
ast_expression_codegen *cgen;
|
||||
ir_value *leftl, *leftr, *right, *bin;
|
||||
|
||||
if (lvalue && self->expression.outl) {
|
||||
*out = self->expression.outl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lvalue && self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* for a binstore we need both an lvalue and an rvalue for the left side */
|
||||
/* rvalue of destination! */
|
||||
cgen = self->dest->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftr))
|
||||
return false;
|
||||
|
||||
/* source as rvalue only */
|
||||
cgen = self->source->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
|
||||
return false;
|
||||
|
||||
/* now the binary */
|
||||
bin = ir_block_create_binop(func->curblock, ast_function_label(func, "binst"),
|
||||
self->opbin, leftr, right);
|
||||
self->expression.outr = bin;
|
||||
|
||||
/* now store them */
|
||||
cgen = self->dest->expression.codegen;
|
||||
/* lvalue of destination */
|
||||
if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
|
||||
return false;
|
||||
self->expression.outl = leftl;
|
||||
|
||||
if (!ir_block_create_store_op(func->curblock, self->opstore, leftl, bin))
|
||||
return false;
|
||||
self->expression.outr = bin;
|
||||
|
||||
/* Theoretically, an assinment returns its left side as an
|
||||
* lvalue, if we don't need an lvalue though, we return
|
||||
* the right side as an rvalue, otherwise we have to
|
||||
* somehow know whether or not we need to dereference the pointer
|
||||
* on the left side - that is: OP_LOAD if it was an address.
|
||||
* Also: in original QC we cannot OP_LOADP *anyway*.
|
||||
*/
|
||||
*out = (lvalue ? leftl : bin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -916,6 +1028,10 @@ bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_valu
|
|||
* the lvalue flag.
|
||||
*/
|
||||
(void)lvalue;
|
||||
if (self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->operand->expression.codegen;
|
||||
/* lvalue! */
|
||||
|
@ -926,6 +1042,7 @@ bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_valu
|
|||
self->op, operand);
|
||||
if (!*out)
|
||||
return false;
|
||||
self->expression.outr = *out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -939,6 +1056,11 @@ bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_va
|
|||
* the lvalue flag.
|
||||
*/
|
||||
(void)lvalue;
|
||||
if (self->expression.outr) {
|
||||
printf("internal error: ast_return cannot be reused, it bears no result!\n");
|
||||
return false;
|
||||
}
|
||||
self->expression.outr = (ir_value*)1;
|
||||
|
||||
cgen = self->operand->expression.codegen;
|
||||
/* lvalue! */
|
||||
|
@ -961,6 +1083,16 @@ bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, i
|
|||
* value in a temp.
|
||||
*/
|
||||
|
||||
if (lvalue && self->expression.outl) {
|
||||
*out = self->expression.outl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lvalue && self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->entity->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent))
|
||||
return false;
|
||||
|
@ -980,6 +1112,11 @@ bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, i
|
|||
if (!*out)
|
||||
return false;
|
||||
|
||||
if (lvalue)
|
||||
self->expression.outl = *out;
|
||||
else
|
||||
self->expression.outr = *out;
|
||||
|
||||
/* Hm that should be it... */
|
||||
return true;
|
||||
}
|
||||
|
@ -989,6 +1126,13 @@ bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_va
|
|||
ast_expression_codegen *cgen;
|
||||
ir_value *vec;
|
||||
|
||||
/* in QC this is always an lvalue */
|
||||
(void)lvalue;
|
||||
if (self->expression.outl) {
|
||||
*out = self->expression.outl;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->owner->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->owner), func, true, &vec))
|
||||
return false;
|
||||
|
@ -1000,6 +1144,7 @@ bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_va
|
|||
}
|
||||
|
||||
*out = ir_value_vector_member(vec, self->field);
|
||||
self->expression.outl = *out;
|
||||
|
||||
return (*out != NULL);
|
||||
}
|
||||
|
@ -1020,6 +1165,12 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
|
|||
(void)out;
|
||||
(void)lvalue;
|
||||
|
||||
if (self->expression.outr) {
|
||||
printf("internal error: ast_ifthen cannot be reused, it bears no result!\n");
|
||||
return false;
|
||||
}
|
||||
self->expression.outr = (ir_value*)1;
|
||||
|
||||
/* generate the condition */
|
||||
func->curblock = cond;
|
||||
cgen = self->cond->expression.codegen;
|
||||
|
@ -1100,6 +1251,10 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
|
|||
ir_block *onfalse;
|
||||
ir_block *merge;
|
||||
|
||||
/* Ternary can never create an lvalue... */
|
||||
if (lvalue)
|
||||
return false;
|
||||
|
||||
/* In theory it shouldn't be possible to pass through a node twice, but
|
||||
* in case we add any kind of optimization pass for the AST itself, it
|
||||
* may still happen, thus we remember a created ir_value and simply return one
|
||||
|
@ -1110,10 +1265,6 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Ternary can never create an lvalue... */
|
||||
if (lvalue)
|
||||
return false;
|
||||
|
||||
/* In the following, contraty to ast_ifthen, we assume both paths exist. */
|
||||
|
||||
/* generate the condition */
|
||||
|
@ -1225,6 +1376,12 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
|
|||
(void)lvalue;
|
||||
(void)out;
|
||||
|
||||
if (self->expression.outr) {
|
||||
printf("internal error: ast_loop cannot be reused, it bears no result!\n");
|
||||
return false;
|
||||
}
|
||||
self->expression.outr = (ir_value*)1;
|
||||
|
||||
/* NOTE:
|
||||
* Should we ever need some kind of block ordering, better make this function
|
||||
* move blocks around than write a block ordering algorithm later... after all
|
||||
|
@ -1429,9 +1586,14 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
|
|||
|
||||
ir_value *funval = NULL;
|
||||
|
||||
/* return values are never rvalues */
|
||||
/* return values are never lvalues */
|
||||
(void)lvalue;
|
||||
|
||||
if (self->expression.outr) {
|
||||
*out = self->expression.outr;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->func->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->func), func, false, &funval))
|
||||
return false;
|
||||
|
@ -1465,6 +1627,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
|
|||
}
|
||||
|
||||
*out = ir_call_value(callinstr);
|
||||
self->expression.outr = *out;
|
||||
|
||||
MEM_VECTOR_CLEAR(¶ms, v);
|
||||
return true;
|
||||
|
|
32
ast.h
32
ast.h
|
@ -36,6 +36,7 @@ typedef struct ast_function_s ast_function;
|
|||
typedef struct ast_block_s ast_block;
|
||||
typedef struct ast_binary_s ast_binary;
|
||||
typedef struct ast_store_s ast_store;
|
||||
typedef struct ast_binstore_s ast_binstore;
|
||||
typedef struct ast_entfield_s ast_entfield;
|
||||
typedef struct ast_ifthen_s ast_ifthen;
|
||||
typedef struct ast_ternary_s ast_ternary;
|
||||
|
@ -53,6 +54,7 @@ enum {
|
|||
TYPE_ast_block,
|
||||
TYPE_ast_binary,
|
||||
TYPE_ast_store,
|
||||
TYPE_ast_binstore,
|
||||
TYPE_ast_entfield,
|
||||
TYPE_ast_ifthen,
|
||||
TYPE_ast_ternary,
|
||||
|
@ -104,6 +106,13 @@ typedef struct
|
|||
int vtype;
|
||||
ast_expression *next;
|
||||
MEM_VECTOR_MAKE(ast_value*, params);
|
||||
/* 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;
|
||||
} ast_expression_common;
|
||||
MEM_VECTOR_PROTO(ast_expression_common, ast_value*, params);
|
||||
|
||||
|
@ -170,6 +179,29 @@ void ast_binary_delete(ast_binary*);
|
|||
|
||||
bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
|
||||
|
||||
/* 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_s
|
||||
{
|
||||
ast_expression_common expression;
|
||||
|
||||
int opstore;
|
||||
int opbin;
|
||||
ast_expression *dest;
|
||||
ast_expression *source;
|
||||
};
|
||||
ast_binstore* ast_binstore_new(lex_ctx ctx,
|
||||
int storeop,
|
||||
int op,
|
||||
ast_expression *left,
|
||||
ast_expression *right);
|
||||
void ast_binstore_delete(ast_binstore*);
|
||||
|
||||
bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
|
||||
|
||||
/* Unary
|
||||
*
|
||||
* Regular unary expressions: not,neg
|
||||
|
|
Loading…
Reference in a new issue