mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-17 17:11:32 +00:00
Merge branch 'master' of github.com:graphitemaster/gmqcc
This commit is contained in:
commit
d700bb66b2
7 changed files with 109 additions and 6 deletions
10
ast.c
10
ast.c
|
@ -570,7 +570,8 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
|
|||
self->expression.next = ast_shallow_type(ctx, TYPE_FLOAT);
|
||||
}
|
||||
|
||||
self->owner = owner;
|
||||
self->rvalue = false;
|
||||
self->owner = owner;
|
||||
ast_propagate_effects(self, owner);
|
||||
|
||||
self->field = field;
|
||||
|
@ -2082,14 +2083,17 @@ bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_va
|
|||
ir_value *vec;
|
||||
|
||||
/* in QC this is always an lvalue */
|
||||
(void)lvalue;
|
||||
if (lvalue && self->rvalue) {
|
||||
compile_error(ast_ctx(self), "not an l-value (member access)");
|
||||
return false;
|
||||
}
|
||||
if (self->expression.outl) {
|
||||
*out = self->expression.outl;
|
||||
return true;
|
||||
}
|
||||
|
||||
cgen = self->owner->expression.codegen;
|
||||
if (!(*cgen)((ast_expression*)(self->owner), func, true, &vec))
|
||||
if (!(*cgen)((ast_expression*)(self->owner), func, false, &vec))
|
||||
return false;
|
||||
|
||||
if (vec->vtype != TYPE_VECTOR &&
|
||||
|
|
1
ast.h
1
ast.h
|
@ -324,6 +324,7 @@ struct ast_member_s
|
|||
ast_expression *owner;
|
||||
unsigned int field;
|
||||
const char *name;
|
||||
bool rvalue;
|
||||
};
|
||||
ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name);
|
||||
void ast_member_delete(ast_member*);
|
||||
|
|
|
@ -490,6 +490,15 @@ instruction is added for several reasons. (For example the qcvm's
|
|||
disassemble switch uses it to know when the function ends.). This
|
||||
optimization replaces that last RETURN with DONE rather than adding
|
||||
the DONE additionally.
|
||||
.TP
|
||||
.B -Ovector-components
|
||||
Because traditional QC code doesn't allow you to access individual
|
||||
vector components of a computed vector without storing it in a local
|
||||
first, sometimes people multiply it by a constant like '0 1 0' to get,
|
||||
in this case, the y component of a vector. This optimization will turn
|
||||
such a multiplication into a direct component access. If the factor is
|
||||
anything other than 1, a float-multiplication will be added, which is
|
||||
still faster than a vector multiplication.
|
||||
.SH CONFIG
|
||||
The configuration file is similar to regular .ini files. Comments
|
||||
start with hashtags or semicolons, sections are written in square
|
||||
|
|
|
@ -232,3 +232,7 @@
|
|||
|
||||
# Do not create a RETURN instruction at the end functions of return-type void.
|
||||
VOID_RETURN = true
|
||||
|
||||
# Turn extraction-multiplications such as (a_vector * '0 1 0')
|
||||
# into direct component accesses
|
||||
VECTOR_COMPONENTS = true
|
||||
|
|
32
ir.c
32
ir.c
|
@ -2310,6 +2310,9 @@ bool ir_function_allocate_locals(ir_function *self)
|
|||
if (vec_size(v->writes) == 1 && v->writes[0]->opcode == INSTR_CALL0)
|
||||
{
|
||||
v->store = store_return;
|
||||
if (v->members[0]) v->members[0]->store = store_return;
|
||||
if (v->members[1]) v->members[1]->store = store_return;
|
||||
if (v->members[2]) v->members[2]->store = store_return;
|
||||
++opts_optimizationcount[OPTIM_CALL_STORES];
|
||||
continue;
|
||||
}
|
||||
|
@ -3779,7 +3782,7 @@ void ir_function_dump(ir_function *f, char *ind,
|
|||
attr = "unique ";
|
||||
else if (v->locked)
|
||||
attr = "locked ";
|
||||
oprintf("%s\t%s: %s@%i ", ind, v->name, attr, (int)v->code.local);
|
||||
oprintf("%s\t%s: %s %s@%i ", ind, v->name, type_name[v->vtype], attr, (int)v->code.local);
|
||||
for (l = 0; l < vec_size(v->life); ++l) {
|
||||
oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
|
||||
}
|
||||
|
@ -3802,13 +3805,36 @@ void ir_function_dump(ir_function *f, char *ind,
|
|||
}
|
||||
}
|
||||
for (i = 0; i < vec_size(f->values); ++i) {
|
||||
size_t l;
|
||||
const char *attr = "";
|
||||
size_t l, m;
|
||||
ir_value *v = f->values[i];
|
||||
oprintf("%s\t%s: @%i ", ind, v->name, (int)v->code.local);
|
||||
if (v->unique_life && v->locked)
|
||||
attr = "unique,locked ";
|
||||
else if (v->unique_life)
|
||||
attr = "unique ";
|
||||
else if (v->locked)
|
||||
attr = "locked ";
|
||||
oprintf("%s\t%s: %s %s@%i ", ind, v->name, type_name[v->vtype], attr, (int)v->code.local);
|
||||
for (l = 0; l < vec_size(v->life); ++l) {
|
||||
oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
|
||||
}
|
||||
oprintf("\n");
|
||||
for (m = 0; m < 3; ++m) {
|
||||
ir_value *vm = v->members[m];
|
||||
if (!vm)
|
||||
continue;
|
||||
if (vm->unique_life && vm->locked)
|
||||
attr = "unique,locked ";
|
||||
else if (vm->unique_life)
|
||||
attr = "unique ";
|
||||
else if (vm->locked)
|
||||
attr = "locked ";
|
||||
oprintf("%s\t%s: %s@%i ", ind, vm->name, attr, (int)vm->code.local);
|
||||
for (l = 0; l < vec_size(vm->life); ++l) {
|
||||
oprintf("[%i,%i] ", vm->life[l].start, vm->life[l].end);
|
||||
}
|
||||
oprintf("\n");
|
||||
}
|
||||
}
|
||||
if (vec_size(f->blocks))
|
||||
{
|
||||
|
|
1
opts.def
1
opts.def
|
@ -95,6 +95,7 @@
|
|||
GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2)
|
||||
GMQCC_DEFINE_FLAG(CALL_STORES, 1)
|
||||
GMQCC_DEFINE_FLAG(VOID_RETURN, 1)
|
||||
GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1)
|
||||
#endif
|
||||
|
||||
/* some cleanup so we don't have to */
|
||||
|
|
58
parser.c
58
parser.c
|
@ -800,6 +800,64 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
{
|
||||
if (CanConstFold(exprs[0], exprs[1]))
|
||||
out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1)));
|
||||
else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) {
|
||||
vector vec = ConstV(0);
|
||||
if (!vec.y && !vec.z) { /* 'n 0 0' * v */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.x != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out);
|
||||
}
|
||||
else if (!vec.x && !vec.z) { /* '0 n 0' * v */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.y != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out);
|
||||
}
|
||||
else if (!vec.x && !vec.y) { /* '0 n 0' * v */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.z != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out);
|
||||
}
|
||||
else
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
|
||||
}
|
||||
else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) {
|
||||
vector vec = ConstV(1);
|
||||
if (!vec.y && !vec.z) { /* v * 'n 0 0' */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.x != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x));
|
||||
}
|
||||
else if (!vec.x && !vec.z) { /* v * '0 n 0' */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.y != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y));
|
||||
}
|
||||
else if (!vec.x && !vec.y) { /* v * '0 n 0' */
|
||||
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
|
||||
out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
|
||||
out->expression.node.keep = false;
|
||||
((ast_member*)out)->rvalue = true;
|
||||
if (vec.z != 1)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z));
|
||||
}
|
||||
else
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
|
||||
}
|
||||
else
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue