diff options
Diffstat (limited to 'gcc/config/i960/i960.c')
-rw-r--r-- | gcc/config/i960/i960.c | 244 |
1 files changed, 187 insertions, 57 deletions
diff --git a/gcc/config/i960/i960.c b/gcc/config/i960/i960.c index 9fa8889c301..3b79d644bdc 100644 --- a/gcc/config/i960/i960.c +++ b/gcc/config/i960/i960.c @@ -575,7 +575,7 @@ i960_address_cost (x) Return 1 if we have written out everything that needs to be done to do the move. Otherwise, return 0 and the caller will emit the move - normally. */ + normally. */ int emit_move_sequence (operands, mode) @@ -583,8 +583,12 @@ emit_move_sequence (operands, mode) enum machine_mode mode; { /* We can only store registers to memory. */ - - if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG) + + if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG + && (operands[1] != const0_rtx || current_function_args_size + || current_function_varargs || current_function_stdarg + || rtx_equal_function_value_matters)) + /* Here we use the same test as movsi+1 pattern -- see i960.md. */ operands[1] = force_reg (mode, operands[1]); /* Storing multi-word values in unaligned hard registers to memory may @@ -673,8 +677,13 @@ i960_output_move_double (dst, src) { if (REGNO (src) & 1) { - /* This is handled by emit_move_sequence so we shouldn't get here. */ - abort (); + operands[0] = dst; + operands[1] = adj_offsettable_operand (dst, UNITS_PER_WORD); + if (! memory_address_p (word_mode, XEXP (operands[1], 0))) + abort (); + operands[2] = src; + output_asm_insn ("st %2,%0\n\tst %D2,%1", operands); + return ""; } return "stl %1,%0"; } @@ -682,6 +691,22 @@ i960_output_move_double (dst, src) abort (); } +/* Output assembler to move a double word zero. */ + +char * +i960_output_move_double_zero (dst) + rtx dst; +{ + rtx operands[2]; + + operands[0] = dst; + { + operands[1] = adj_offsettable_operand (dst, 4); + output_asm_insn ("st g14,%0\n\tst g14,%1", operands); + } + return ""; +} + /* Output assembler to move a quad word value. */ char * @@ -744,14 +769,40 @@ i960_output_move_quad (dst, src) { if (REGNO (src) & 3) { - /* This is handled by emit_move_sequence so we shouldn't get here. */ - abort (); + operands[0] = dst; + operands[1] = adj_offsettable_operand (dst, UNITS_PER_WORD); + operands[2] = adj_offsettable_operand (dst, 2*UNITS_PER_WORD); + operands[3] = adj_offsettable_operand (dst, 3*UNITS_PER_WORD); + if (! memory_address_p (word_mode, XEXP (operands[3], 0))) + abort (); + operands[4] = src; + output_asm_insn ("st %4,%0\n\tst %D4,%1\n\tst %E4,%2\n\tst %F4,%3", operands); + return ""; } return "stq %1,%0"; } else abort (); } + +/* Output assembler to move a quad word zero. */ + +char * +i960_output_move_quad_zero (dst) + rtx dst; +{ + rtx operands[4]; + + operands[0] = dst; + { + operands[1] = adj_offsettable_operand (dst, 4); + operands[2] = adj_offsettable_operand (dst, 8); + operands[3] = adj_offsettable_operand (dst, 12); + output_asm_insn ("st g14,%0\n\tst g14,%1\n\tst g14,%2\n\tst g14,%3", operands); + } + return ""; +} + /* Emit insns to load a constant to non-floating point registers. Uses several strategies to try to use as few insns as possible. */ @@ -1184,6 +1235,95 @@ compute_frame_size (size) return actual_fsize; } +/* Here register group is range of registers which can be moved by + one i960 instruction. */ + +struct reg_group +{ + char start_reg; + char length; +}; + +/* The following functions forms the biggest as possible register + groups with registers in STATE. REGS contain states of the + registers in range [start, finish_reg). The function returns the + number of groups formed. */ +static int +i960_form_reg_groups (start_reg, finish_reg, regs, state, reg_groups) + int start_reg; + int finish_reg; + int *regs; + int state; + struct reg_group *reg_groups; +{ + int i; + int nw = 0; + + for (i = start_reg; i < finish_reg; ) + { + if (regs [i] != state) + { + i++; + continue; + } + else if (i % 2 != 0 || regs [i + 1] != state) + reg_groups [nw].length = 1; + else if (i % 4 != 0 || regs [i + 2] != state) + reg_groups [nw].length = 2; + else if (regs [i + 3] != state) + reg_groups [nw].length = 3; + else + reg_groups [nw].length = 4; + reg_groups [nw].start_reg = i; + i += reg_groups [nw].length; + nw++; + } + return nw; +} + +/* We sort register winodws in descending order by length. */ +static int +i960_reg_group_compare (group1, group2) + void *group1; + void *group2; +{ + struct reg_group *w1 = group1; + struct reg_group *w2 = group2; + + if (w1->length > w2->length) + return -1; + else if (w1->length < w2->length) + return 1; + else + return 0; +} + +/* Split the first register group in REG_GROUPS on subgroups one of + which will contain SUBGROUP_LENGTH registers. The function + returns new number of winodws. */ +static int +i960_split_reg_group (reg_groups, nw, subgroup_length) + struct reg_group *reg_groups; + int nw; + int subgroup_length; +{ + if (subgroup_length < reg_groups->length - subgroup_length) + /* This guarantees correct alignments of the two subgroups for + i960 (see spliting for the group length 2, 3, 4). More + generalized algorithm would require splitting the group more + two subgroups. */ + subgroup_length = reg_groups->length - subgroup_length; + /* More generalized algorithm would require to try merging + subgroups here. But in case i960 it always results in failure + because of register group alignment. */ + reg_groups[nw].length = reg_groups->length - subgroup_length; + reg_groups[nw].start_reg = reg_groups->start_reg + subgroup_length; + nw++; + reg_groups->length = subgroup_length; + qsort (reg_groups, nw, sizeof (struct reg_group), i960_reg_group_compare); + return nw; +} + /* Output code for the function prologue. */ void @@ -1195,10 +1335,17 @@ i960_function_prologue (file, size) int n_iregs = 0; int rsize = 0; int actual_fsize, offset; + int gnw, lnw; + struct reg_group *g, *l; char tmpstr[1000]; /* -1 if reg must be saved on proc entry, 0 if available, 1 if saved somewhere. */ int regs[FIRST_PSEUDO_REGISTER]; + /* All global registers (which must be saved) divided by groups. */ + struct reg_group global_reg_groups [16]; + /* All local registers (which are available) divided by groups. */ + struct reg_group local_reg_groups [16]; + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regs_ever_live[i] @@ -1226,62 +1373,43 @@ i960_function_prologue (file, size) regs[i] = -1; } - /* First look for local registers to save globals in. */ - for (i = 0; i < 16; i++) + gnw = i960_form_reg_groups (0, 16, regs, -1, global_reg_groups); + lnw = i960_form_reg_groups (19, 32, regs, 0, local_reg_groups); + qsort (global_reg_groups, gnw, sizeof (struct reg_group), + i960_reg_group_compare); + qsort (local_reg_groups, lnw, sizeof (struct reg_group), + i960_reg_group_compare); + for (g = global_reg_groups, l = local_reg_groups; lnw != 0 && gnw != 0;) { - if (regs[i] == 0) - continue; - - /* Start at r4, not r3. */ - for (j = 20; j < 32; j++) + if (g->length == l->length) { - if (regs[j] != 0) - continue; - - regs[i] = 1; - regs[j] = -1; - regs_ever_live[j] = 1; - nr = 1; - if (i <= 14 && i % 2 == 0 && j <= 30 && j % 2 == 0 - && regs[i+1] != 0 && regs[j+1] == 0) - { - nr = 2; - regs[i+1] = 1; - regs[j+1] = -1; - regs_ever_live[j+1] = 1; - } - if (nr == 2 && i <= 12 && i % 4 == 0 && j <= 28 && j % 4 == 0 - && regs[i+2] != 0 && regs[j+2] == 0) - { - nr = 3; - regs[i+2] = 1; - regs[j+2] = -1; - regs_ever_live[j+2] = 1; - } - if (nr == 3 && regs[i+3] != 0 && regs[j+3] == 0) - { - nr = 4; - regs[i+3] = 1; - regs[j+3] = -1; - regs_ever_live[j+3] = 1; - } - fprintf (file, "\tmov%s %s,%s\n", - ((nr == 4) ? "q" : - (nr == 3) ? "t" : - (nr == 2) ? "l" : ""), - reg_names[i], reg_names[j]); + ((g->length == 4) ? "q" : + (g->length == 3) ? "t" : + (g->length == 2) ? "l" : ""), + reg_names[g->start_reg], reg_names[l->start_reg]); sprintf (tmpstr, "\tmov%s %s,%s\n", - ((nr == 4) ? "q" : - (nr == 3) ? "t" : - (nr == 2) ? "l" : ""), - reg_names[j], reg_names[i]); + ((g->length == 4) ? "q" : + (g->length == 3) ? "t" : + (g->length == 2) ? "l" : ""), + reg_names[l->start_reg], reg_names[g->start_reg]); strcat (epilogue_string, tmpstr); - - n_iregs -= nr; - i += nr-1; - break; + n_iregs -= g->length; + for (i = 0; i < g->length; i++) + { + regs [i + g->start_reg] = 1; + regs [i + l->start_reg] = -1; + regs_ever_live [i + l->start_reg] = 1; + } + g++; + l++; + gnw--; + lnw--; } + else if (g->length > l->length) + gnw = i960_split_reg_group (g, gnw, l->length); + else + lnw = i960_split_reg_group (l, lnw, g->length); } /* N_iregs is now the number of global registers that haven't been saved @@ -1314,6 +1442,8 @@ i960_function_prologue (file, size) into account, but store them before the argument block area. */ offset = 64 + actual_fsize - compute_frame_size (0) - rsize; /* Save registers on stack if needed. */ + /* ??? Is it worth to use the same algorithm as one for saving + global registers in local registers? */ for (i = 0, j = n_iregs; j > 0 && i < 16; i++) { if (regs[i] != -1) |