diff --git a/mach/proto/mcg/mcg.h b/mach/proto/mcg/mcg.h index dd810e40d..06d444d61 100644 --- a/mach/proto/mcg/mcg.h +++ b/mach/proto/mcg/mcg.h @@ -112,6 +112,7 @@ extern void pass_infer_types(void); extern void pass_insert_moves(void); extern void pass_instruction_selector(void); extern void pass_live_vreg_analysis(void); +extern void pass_lower_pushes(void); extern void pass_add_prologue_epilogue(void); extern void pass_register_allocator(void); extern void pass_remove_dead_blocks(void); diff --git a/mach/proto/mcg/pass_lowerpushes.c b/mach/proto/mcg/pass_lowerpushes.c new file mode 100644 index 000000000..c990f862e --- /dev/null +++ b/mach/proto/mcg/pass_lowerpushes.c @@ -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; iblocks.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 diff --git a/mach/proto/mcg/procedure.c b/mach/proto/mcg/procedure.c index abd94d19b..d8b5fd18c 100644 --- a/mach/proto/mcg/procedure.c +++ b/mach/proto/mcg/procedure.c @@ -199,6 +199,9 @@ void procedure_compile(struct procedure* proc) print_blocks('3'); pass_wire_up_return_values(); pass_convert_stack_ops(); + #if defined MCGG_OPTION_LOWER_PUSHES_TO_LOADS_AND_STORES + pass_lower_pushes(); + #endif print_blocks('4'); pass_convert_locals_to_ssa(); print_blocks('5'); diff --git a/util/mcgg/gram.y b/util/mcgg/gram.y index 891030a10..18b88aec5 100644 --- a/util/mcgg/gram.y +++ b/util/mcgg/gram.y @@ -36,6 +36,7 @@ extern int yylex(void); %term FRAGMENT %term NAMED %term NOTEQUALS +%term OPTIONS %term PATTERNS %term PREFERS %term PRESERVED @@ -66,11 +67,27 @@ extern int yylex(void); %% spec - : REGISTERS registers + : optionaloptions + REGISTERS registers DECLARATIONS declarations PATTERNS patterns ; +optionaloptions + : /* nothing */ + | OPTIONS options + ; + +options + : /* nothing */ + | options option ';' + | option ';' + ; + +option + : ID { option($1); } + ; + registers : /* nothing */ | registers register ';' diff --git a/util/mcgg/iburg.c b/util/mcgg/iburg.c index 9979627c0..cd5be61af 100644 --- a/util/mcgg/iburg.c +++ b/util/mcgg/iburg.c @@ -836,6 +836,11 @@ static void emitheader(void) 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 */ static char* computekids(Tree node, const char* v, char* bp, int* ip) { diff --git a/util/mcgg/iburg.h b/util/mcgg/iburg.h index 739641d4d..199d1aa83 100644 --- a/util/mcgg/iburg.h +++ b/util/mcgg/iburg.h @@ -72,6 +72,7 @@ struct regattr int number; /* identifying number */ }; +extern void option(const char* name); extern struct reg* makereg(const char* name); extern void setregnames(struct reg* reg, struct stringlist* names); extern void addregattr(struct reg* reg, const char* regattr); diff --git a/util/mcgg/ir.dat b/util/mcgg/ir.dat index 4b7c4e920..d32e642ac 100644 --- a/util/mcgg/ir.dat +++ b/util/mcgg/ir.dat @@ -1,7 +1,7 @@ -# Types: +# The second column indicates the type inference rules: # # 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 # ?: pull/push types from other ? parameters diff --git a/util/mcgg/scan.l b/util/mcgg/scan.l index 9d06d5b38..5e61f31f9 100644 --- a/util/mcgg/scan.l +++ b/util/mcgg/scan.l @@ -40,6 +40,7 @@ static int braces = 0; "DECLARATIONS" return DECLARATIONS; "PATTERNS" return PATTERNS; "REGISTERS" return REGISTERS; +"OPTIONS" return OPTIONS; "aliases" return ALIASES; "corrupted" return CORRUPTED; "cost" return COST;