aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/i960/i960.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i960/i960.c')
-rw-r--r--gcc/config/i960/i960.c244
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)