Add the OPTIONS clause to the mcgg grammar; add an optional pass which converts
sequences of PUSHes to a single STACKADJUST followed by STOREs. This should dramatically improve code on stack-unfriendly architectures like MIPS.
This commit is contained in:
parent
985d3dc7d1
commit
bbb708717a
|
@ -112,6 +112,7 @@ extern void pass_infer_types(void);
|
||||||
extern void pass_insert_moves(void);
|
extern void pass_insert_moves(void);
|
||||||
extern void pass_instruction_selector(void);
|
extern void pass_instruction_selector(void);
|
||||||
extern void pass_live_vreg_analysis(void);
|
extern void pass_live_vreg_analysis(void);
|
||||||
|
extern void pass_lower_pushes(void);
|
||||||
extern void pass_add_prologue_epilogue(void);
|
extern void pass_add_prologue_epilogue(void);
|
||||||
extern void pass_register_allocator(void);
|
extern void pass_register_allocator(void);
|
||||||
extern void pass_remove_dead_blocks(void);
|
extern void pass_remove_dead_blocks(void);
|
||||||
|
|
140
mach/proto/mcg/pass_lowerpushes.c
Normal file
140
mach/proto/mcg/pass_lowerpushes.c
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include "mcg.h"
|
||||||
|
|
||||||
|
#if defined MCGG_OPTION_LOWER_PUSHES_TO_LOADS_AND_STORES
|
||||||
|
|
||||||
|
/* On architectures which can't push and pop cheaply, a push typically
|
||||||
|
*
|
||||||
|
* sub sp, sp, -4
|
||||||
|
* sw r5, 0(sp)
|
||||||
|
*
|
||||||
|
* This is hugely wasteful when you want to push or multiple things
|
||||||
|
* at once, which happens a lot because that's how the procedure calling
|
||||||
|
* convention works. This code detects these runs and turns them into a
|
||||||
|
* single stack adjustment and then offsetted accesses via the stack
|
||||||
|
* pointer. In order to be efficient, the table needs to know how to
|
||||||
|
* handle this efficiently:
|
||||||
|
*
|
||||||
|
* STACKADJUST(CONST(-4))
|
||||||
|
* STORE.I(ADD.I(GETSP(), CONST(0)))
|
||||||
|
*
|
||||||
|
* ...otherwise the code will be *even worse*.
|
||||||
|
*
|
||||||
|
* We have to be careful, though, because after we do the adjustment,
|
||||||
|
* the physical stack pointer won't match em's idea of the stack pointer
|
||||||
|
* until the last 'push' happens. So we need to check that this is never
|
||||||
|
* used.
|
||||||
|
*
|
||||||
|
* With this option set, PUSH will never be seen by the instruction
|
||||||
|
* selector.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct basicblock* current_bb;
|
||||||
|
static int cursor;
|
||||||
|
|
||||||
|
static bool accesses_stack_pointer_cb(struct ir* ir, void* user)
|
||||||
|
{
|
||||||
|
switch (ir->opcode)
|
||||||
|
{
|
||||||
|
case IR_SETSP:
|
||||||
|
case IR_GETSP:
|
||||||
|
case IR_CALL:
|
||||||
|
case IR_STACKADJUST:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool accesses_stack_pointer(struct ir* ir)
|
||||||
|
{
|
||||||
|
return !!ir_walk(ir, accesses_stack_pointer_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void consider_push(struct ir* ir)
|
||||||
|
{
|
||||||
|
int runstart;
|
||||||
|
int delta;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (ir->opcode != IR_PUSH)
|
||||||
|
{
|
||||||
|
cursor++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the first push of a run; we'll want to insert the STACKADJUST
|
||||||
|
* before this one. */
|
||||||
|
|
||||||
|
tracef('P', "found push in %s at IR index %d\n", current_bb->name, cursor);
|
||||||
|
runstart = cursor;
|
||||||
|
|
||||||
|
/* Now start walking forward until we find an IR which isn't a safe push.
|
||||||
|
* The current IR is always safe. */
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
struct ir* ir;
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
if (cursor == current_bb->irs.count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ir = current_bb->irs.item[cursor];
|
||||||
|
if (ir->opcode != IR_PUSH)
|
||||||
|
break;
|
||||||
|
if (accesses_stack_pointer(ir))
|
||||||
|
break;
|
||||||
|
|
||||||
|
delta += ir->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef('P', "found end of run at IR index %d\n", cursor);
|
||||||
|
|
||||||
|
/* Now work backwards, converting each push into a stack write. */
|
||||||
|
|
||||||
|
delta = 0;
|
||||||
|
i = cursor - 1;
|
||||||
|
while (i >= runstart)
|
||||||
|
{
|
||||||
|
struct ir* ir = current_bb->irs.item[i];
|
||||||
|
struct ir* value_ir = ir->left;
|
||||||
|
assert(ir->opcode == IR_PUSH);
|
||||||
|
|
||||||
|
ir->opcode = IR_STORE;
|
||||||
|
ir->left = new_ir2(
|
||||||
|
IR_ADD, EM_pointersize,
|
||||||
|
new_ir0(IR_GETSP, EM_pointersize),
|
||||||
|
new_wordir(delta)
|
||||||
|
);
|
||||||
|
ir->left->root = ir->left->left->root = ir->left->right->root = ir->root;
|
||||||
|
ir->right = value_ir;
|
||||||
|
|
||||||
|
delta += ir->size;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And finally, before the place where the first push was, adjust the
|
||||||
|
* stack. */
|
||||||
|
|
||||||
|
ir = new_ir1(IR_STACKADJUST, EM_pointersize, new_wordir(-delta));
|
||||||
|
ir->left->root = ir->root = ir;
|
||||||
|
array_insert(¤t_bb->irs, ir, runstart);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pass_lower_pushes(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i<current_proc->blocks.count; i++)
|
||||||
|
{
|
||||||
|
current_bb = current_proc->blocks.item[i];
|
||||||
|
|
||||||
|
cursor = 0;
|
||||||
|
while (cursor < current_bb->irs.count)
|
||||||
|
consider_push(current_bb->irs.item[cursor]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -199,6 +199,9 @@ void procedure_compile(struct procedure* proc)
|
||||||
print_blocks('3');
|
print_blocks('3');
|
||||||
pass_wire_up_return_values();
|
pass_wire_up_return_values();
|
||||||
pass_convert_stack_ops();
|
pass_convert_stack_ops();
|
||||||
|
#if defined MCGG_OPTION_LOWER_PUSHES_TO_LOADS_AND_STORES
|
||||||
|
pass_lower_pushes();
|
||||||
|
#endif
|
||||||
print_blocks('4');
|
print_blocks('4');
|
||||||
pass_convert_locals_to_ssa();
|
pass_convert_locals_to_ssa();
|
||||||
print_blocks('5');
|
print_blocks('5');
|
||||||
|
|
|
@ -36,6 +36,7 @@ extern int yylex(void);
|
||||||
%term FRAGMENT
|
%term FRAGMENT
|
||||||
%term NAMED
|
%term NAMED
|
||||||
%term NOTEQUALS
|
%term NOTEQUALS
|
||||||
|
%term OPTIONS
|
||||||
%term PATTERNS
|
%term PATTERNS
|
||||||
%term PREFERS
|
%term PREFERS
|
||||||
%term PRESERVED
|
%term PRESERVED
|
||||||
|
@ -66,11 +67,27 @@ extern int yylex(void);
|
||||||
%%
|
%%
|
||||||
|
|
||||||
spec
|
spec
|
||||||
: REGISTERS registers
|
: optionaloptions
|
||||||
|
REGISTERS registers
|
||||||
DECLARATIONS declarations
|
DECLARATIONS declarations
|
||||||
PATTERNS patterns
|
PATTERNS patterns
|
||||||
;
|
;
|
||||||
|
|
||||||
|
optionaloptions
|
||||||
|
: /* nothing */
|
||||||
|
| OPTIONS options
|
||||||
|
;
|
||||||
|
|
||||||
|
options
|
||||||
|
: /* nothing */
|
||||||
|
| options option ';'
|
||||||
|
| option ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
option
|
||||||
|
: ID { option($1); }
|
||||||
|
;
|
||||||
|
|
||||||
registers
|
registers
|
||||||
: /* nothing */
|
: /* nothing */
|
||||||
| registers register ';'
|
| registers register ';'
|
||||||
|
|
|
@ -836,6 +836,11 @@ static void emitheader(void)
|
||||||
printh("#define MCG_DEFS_H\n\n");
|
printh("#define MCG_DEFS_H\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void option(const char* o)
|
||||||
|
{
|
||||||
|
printh("#define MCGG_OPTION_%s\n", o);
|
||||||
|
}
|
||||||
|
|
||||||
/* computekids - compute paths to kids in tree t */
|
/* computekids - compute paths to kids in tree t */
|
||||||
static char* computekids(Tree node, const char* v, char* bp, int* ip)
|
static char* computekids(Tree node, const char* v, char* bp, int* ip)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,7 @@ struct regattr
|
||||||
int number; /* identifying number */
|
int number; /* identifying number */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern void option(const char* name);
|
||||||
extern struct reg* makereg(const char* name);
|
extern struct reg* makereg(const char* name);
|
||||||
extern void setregnames(struct reg* reg, struct stringlist* names);
|
extern void setregnames(struct reg* reg, struct stringlist* names);
|
||||||
extern void addregattr(struct reg* reg, const char* regattr);
|
extern void addregattr(struct reg* reg, const char* regattr);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Types:
|
# The second column indicates the type inference rules:
|
||||||
#
|
#
|
||||||
# I, F, L, D: int, float, long, double
|
# I, F, L, D: int, float, long, double
|
||||||
# i, F: int, float; promoted to long, double based on size
|
# i, f: int, float; promoted to long, double based on size
|
||||||
# .: ignore this parameter
|
# .: ignore this parameter
|
||||||
# ?: pull/push types from other ? parameters
|
# ?: pull/push types from other ? parameters
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ static int braces = 0;
|
||||||
"DECLARATIONS" return DECLARATIONS;
|
"DECLARATIONS" return DECLARATIONS;
|
||||||
"PATTERNS" return PATTERNS;
|
"PATTERNS" return PATTERNS;
|
||||||
"REGISTERS" return REGISTERS;
|
"REGISTERS" return REGISTERS;
|
||||||
|
"OPTIONS" return OPTIONS;
|
||||||
"aliases" return ALIASES;
|
"aliases" return ALIASES;
|
||||||
"corrupted" return CORRUPTED;
|
"corrupted" return CORRUPTED;
|
||||||
"cost" return COST;
|
"cost" return COST;
|
||||||
|
|
Loading…
Reference in a new issue