mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-23 20:33:05 +00:00
New options: -ftypeless-stores and -fsort-operands
These flags reduce entropy, but not size, of the generated assembly code. This helps compressability of the files. Additionally, -ftypeless-stores might SLIGHTLY improve engine performance due to less instructions being used (so branch prediction might work better). Probably cannot be measured though. Signed-off-by: Rudolf Polzer <divverent@xonotic.org>
This commit is contained in:
parent
fc57fa4064
commit
f4f805f4c9
4 changed files with 100 additions and 2 deletions
76
code.c
76
code.c
|
@ -44,9 +44,81 @@ typedef union {
|
|||
#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
|
||||
#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
|
||||
|
||||
void code_push_statement(code_t *code, prog_section_statement_t *stmt, lex_ctx_t ctx)
|
||||
void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
|
||||
{
|
||||
vec_push(code->statements, *stmt);
|
||||
prog_section_statement_t stmt = *stmt_in;
|
||||
|
||||
if (OPTS_FLAG(TYPELESS_STORES)) {
|
||||
switch (stmt.opcode) {
|
||||
case INSTR_LOAD_S:
|
||||
case INSTR_LOAD_ENT:
|
||||
case INSTR_LOAD_FLD:
|
||||
case INSTR_LOAD_FNC:
|
||||
stmt.opcode = INSTR_LOAD_F;
|
||||
break;
|
||||
case INSTR_STORE_S:
|
||||
case INSTR_STORE_ENT:
|
||||
case INSTR_STORE_FLD:
|
||||
case INSTR_STORE_FNC:
|
||||
stmt.opcode = INSTR_STORE_F;
|
||||
break;
|
||||
case INSTR_STOREP_S:
|
||||
case INSTR_STOREP_ENT:
|
||||
case INSTR_STOREP_FLD:
|
||||
case INSTR_STOREP_FNC:
|
||||
stmt.opcode = INSTR_STOREP_F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (OPTS_FLAG(SORT_OPERANDS)) {
|
||||
switch (stmt.opcode) {
|
||||
#define SINGLE(a) \
|
||||
case INSTR_##a: \
|
||||
if (stmt.o1.u1 < stmt.o2.u1) { \
|
||||
uint16_t x = stmt.o1.u1; stmt.o1.u1 = stmt.o2.u1; stmt.o2.u1 = x; \
|
||||
} \
|
||||
break
|
||||
#define PAIR(a,b) \
|
||||
case INSTR_##a: \
|
||||
if (stmt.o1.u1 < stmt.o2.u1) { \
|
||||
uint16_t x = stmt.o1.u1; stmt.o1.u1 = stmt.o2.u1; stmt.o2.u1 = x; \
|
||||
stmt.opcode = INSTR_##b; \
|
||||
} \
|
||||
break; \
|
||||
case INSTR_##b: \
|
||||
if (stmt.o1.u1 < stmt.o2.u1) { \
|
||||
uint16_t x = stmt.o1.u1; stmt.o1.u1 = stmt.o2.u1; stmt.o2.u1 = x; \
|
||||
stmt.opcode = INSTR_##a; \
|
||||
} \
|
||||
break
|
||||
PAIR(MUL_VF, MUL_FV);
|
||||
PAIR(LT, GT);
|
||||
PAIR(LE, GE);
|
||||
SINGLE(MUL_F);
|
||||
SINGLE(MUL_V);
|
||||
SINGLE(ADD_F);
|
||||
SINGLE(ADD_V);
|
||||
SINGLE(EQ_F);
|
||||
SINGLE(EQ_V);
|
||||
SINGLE(EQ_S);
|
||||
SINGLE(EQ_E);
|
||||
SINGLE(EQ_FNC);
|
||||
SINGLE(NE_F);
|
||||
SINGLE(NE_V);
|
||||
SINGLE(NE_S);
|
||||
SINGLE(NE_E);
|
||||
SINGLE(NE_FNC);
|
||||
SINGLE(AND);
|
||||
SINGLE(OR);
|
||||
SINGLE(BITAND);
|
||||
SINGLE(BITOR);
|
||||
#undef PAIR
|
||||
#undef SINGLE
|
||||
}
|
||||
}
|
||||
|
||||
vec_push(code->statements, stmt);
|
||||
vec_push(code->linenums, (int)ctx.line);
|
||||
vec_push(code->columnnums, (int)ctx.column);
|
||||
}
|
||||
|
|
|
@ -535,6 +535,15 @@ When passing on varargs to a different functions, this turns some
|
|||
static error cases into warnings. Like when the caller's varargs are
|
||||
restricted to a different type than the callee's parameter. Or a list
|
||||
of unrestricted varargs is passed into restricted varargs.
|
||||
.It Fl f Ns Cm typeless-stores
|
||||
Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
|
||||
This is somewhat incorrect assembly instruction use, but in all engines
|
||||
they do exactly the same. This makes disassembly output harder to read,
|
||||
breaks decompilers, but causes the output file to be better compressible.
|
||||
.It Fl f Ns Cm sort-operands
|
||||
In commutative instructions, always put the lower-numbered operand first.
|
||||
This shaves off 1 byte of entropy from all these instructions, reducing
|
||||
compressed size of the output file.
|
||||
.El
|
||||
.Sh OPTIMIZATIONS
|
||||
.Bl -tag -width Ds
|
||||
|
|
|
@ -273,6 +273,21 @@
|
|||
UNSAFE_VARARGS = false
|
||||
|
||||
|
||||
#Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
|
||||
#This is somewhat incorrect assembly instruction use, but in all engines
|
||||
#they do exactly the same. This makes disassembly output harder to read,
|
||||
#breaks decompilers, but causes the output file to be better compressible.
|
||||
|
||||
TYPELESS_STORES = false
|
||||
|
||||
|
||||
#In commutative instructions, always put the lower-numbered operand first.
|
||||
#This shaves off 1 byte of entropy from all these instructions, reducing
|
||||
#compressed size of the output file.
|
||||
|
||||
SORT_OPERANDS = false
|
||||
|
||||
|
||||
|
||||
[warnings]
|
||||
#Generate a warning about variables which are declared but never
|
||||
|
|
2
opts.def
2
opts.def
|
@ -53,6 +53,8 @@
|
|||
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
|
||||
GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
|
||||
GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
|
||||
GMQCC_DEFINE_FLAG(TYPELESS_STORES)
|
||||
GMQCC_DEFINE_FLAG(SORT_OPERANDS)
|
||||
#endif
|
||||
|
||||
/* warning flags */
|
||||
|
|
Loading…
Reference in a new issue