#define STAB_CODE_TYPE int
#endif
-extern char *getenv ();
-
-extern char *permalloc ();
-extern void debug_rtx ();
-extern void abort_with_insn ();
-extern rtx copy_to_reg ();
-extern rtx adj_offsettable_operand ();
-extern int offsettable_address_p ();
-extern tree lookup_name ();
-
-extern rtx gen_movqi ();
-extern rtx gen_movhi ();
-extern rtx gen_movsi ();
-extern rtx gen_movsi_ulw ();
-extern rtx gen_movsi_usw ();
-extern rtx gen_addsi3 ();
-extern rtx gen_iorsi3 ();
-extern rtx gen_andsi3 ();
-extern rtx gen_bne ();
-extern rtx gen_beq ();
-extern rtx gen_cmpsi ();
-extern rtx gen_jump ();
-
-extern char call_used_regs[];
-extern char *asm_file_name;
-extern FILE *asm_out_file;
+extern void abort ();
+extern int atoi ();
+extern char *getenv ();
+extern char *mktemp ();
+
+extern rtx adj_offsettable_operand ();
+extern rtx copy_to_reg ();
+extern void error ();
+extern void fatal ();
+extern tree lookup_name ();
+extern void pfatal_with_name ();
+extern void warning ();
+
extern tree current_function_decl;
-extern char **save_argv;
-extern char *version_string;
-extern char *language_string;
+extern FILE *asm_out_file;
+
+/* Enumeration for all of the relational tests, so that we can build
+ arrays indexed by the test type, and not worry about the order
+ of EQ, NE, etc. */
+
+enum internal_test {
+ ITEST_EQ,
+ ITEST_NE,
+ ITEST_GT,
+ ITEST_GE,
+ ITEST_LT,
+ ITEST_LE,
+ ITEST_GTU,
+ ITEST_GEU,
+ ITEST_LTU,
+ ITEST_LEU,
+ ITEST_MAX
+ };
/* Global variables for machine-dependent things. */
/* Count the number of .file directives, so that .loc is up to date. */
int num_source_filenames = 0;
-/* Count of the number of functions created so far, in order to make
- unique labels for omitting the frame pointer. */
-int number_functions_processed = 0;
-
/* Count the number of sdb related labels are generated (to find block
start and end boundaries). */
int sdb_label_count = 0;
int size; /* size in bytes */
} *extern_head = 0;
-/* Name of the current function. */
-char *current_function_name;
-
/* Name of the file containing the current function. */
char *current_function_file = "";
/* what type of branch to use */
enum cmp_type branch_type;
+/* Number of previously seen half-pic pointers and references. */
+static int prev_half_pic_ptrs = 0;
+static int prev_half_pic_refs = 0;
+
/* which cpu are we scheduling for */
enum processor_type mips_cpu;
rtx op;
enum machine_mode mode;
{
- long value;
+ HOST_WIDE_INT value;
if (GET_CODE (op) != CONST_INT)
return FALSE;
value = INTVAL (op);
- if ((value & 0xffff0000) == 0) /* ior reg,$r0,value */
+ if ((value & ~0x0000ffff) == 0) /* ior reg,$r0,value */
return FALSE;
- if ((value & 0xffff0000) == 0xffff0000) /* subu reg,$r0,value */
+ if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */
return FALSE;
- if ((value & 0x0000ffff) == 0) /* lui reg,value>>16 */
+ if ((value & 0xffff0000) == value) /* lui reg,value>>16 */
return FALSE;
return TRUE;
{
switch (GET_CODE (op))
{
+ default:
+ break;
+
case CONST_INT:
return (INTVAL (op) == 0);
&& MD_REG_P (REGNO (op)));
}
+/* Return truth value of whether OP is the FP status register. */
+
+int
+fpsw_register_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == REG && ST_REG_P (REGNO (op)));
+}
+
/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */
int
addr = XEXP (op, 0);
switch (GET_CODE (addr))
{
+ default:
+ break;
+
case REG:
return TRUE;
return FALSE;
}
-/* Return true if the address is suitable for function call. */
-
-int
-call_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx addr;
- enum rtx_code code;
-
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- addr = XEXP (op, 0);
- code = GET_CODE (addr);
-
- if (GET_MODE (addr) != FUNCTION_MODE)
- return FALSE;
-
- if (code == REG || code == SUBREG)
- return TRUE;
-
- if (CONSTANT_ADDRESS_P (addr))
- return TRUE;
-
- return FALSE;
-}
-
/* Return true if the code of this rtx pattern is EQ or NE. */
int
return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
}
+
+/* Return true if the operand is either the PC or a label_ref. */
+
+int
+pc_or_label_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (op == pc_rtx)
+ return TRUE;
+
+ if (GET_CODE (op) == LABEL_REF)
+ return TRUE;
+
+ return FALSE;
+}
+
\f
/* Return an operand string if the given instruction's delay slot or
wrap it in a .set noreorder section. This is for filling delay
register rtx next_insn = (cur_insn) ? NEXT_INSN (cur_insn) : (rtx)0;
register int num_nops;
- if (type == DELAY_LOAD)
+ if (type == DELAY_LOAD || type == DELAY_FCMP)
num_nops = 1;
else if (type == DELAY_HILO)
looping = FALSE;
switch (GET_CODE (addr))
{
+ default:
+ break;
+
case REG:
case CONST_INT:
break;
target, so zero/sign extend can use this code as well. */
switch (GET_MODE (op1))
{
- case SFmode: ret = "lw\t%0,%1"; break;
- case SImode: ret = "lw\t%0,%1"; break;
- case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break;
- case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break;
+ default: break;
+ case SFmode: ret = "lw\t%0,%1"; break;
+ case SImode: ret = "lw\t%0,%1"; break;
+ case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break;
+ case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break;
}
}
else if (FP_REG_P (regno0))
{
delay = DELAY_LOAD;
- return "mtc1\t%z1,%0";
+ ret = "mtc1\t%z1,%0";
}
}
else if (GP_REG_P (regno0))
- {
- if ((INTVAL (operands[1]) & 0x0000ffff) == 0)
- ret = "lui\t%0,(%X1)>>16";
- else
- ret = "li\t%0,%1";
- }
+ ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1";
}
else if (code1 == CONST_DOUBLE && mode == SFmode)
}
else if (code1 == LABEL_REF)
- ret = "la\t%0,%a1";
-
- else if (code1 == SYMBOL_REF || code1 == CONST)
{
if (TARGET_STATS)
mips_count_memory_refs (op1, 1);
+ ret = "la\t%0,%a1";
+ }
+
+ else if (code1 == SYMBOL_REF || code1 == CONST)
+ {
if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1))
{
- delay = DELAY_LOAD;
- ret = "la\t%0,%a1\t\t# pic reference";
+ rtx offset = const0_rtx;
+
+ if (GET_CODE (op1) == CONST)
+ op1 = eliminate_constant_term (XEXP (op1, 0), &offset);
+
+ if (GET_CODE (op1) == SYMBOL_REF)
+ {
+ operands[2] = HALF_PIC_PTR (op1);
+
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[2], 1);
+
+ if (INTVAL (offset) == 0)
+ {
+ delay = DELAY_LOAD;
+ ret = "lw\t%0,%2";
+ }
+ else
+ {
+ dslots_load_total++;
+ operands[3] = offset;
+ ret = (SMALL_INT (offset))
+ ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
+ : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
+ }
+ }
}
else
- ret = "la\t%0,%a1";
+ {
+ if (TARGET_STATS)
+ mips_count_memory_refs (op1, 1);
+
+ ret = "la\t%0,%a1";
+ }
}
else if (code1 == PLUS)
{
switch (mode)
{
+ default: break;
case SFmode: ret = "sw\t%1,%0"; break;
case SImode: ret = "sw\t%1,%0"; break;
case HImode: ret = "sh\t%1,%0"; break;
{
switch (mode)
{
+ default: break;
case SFmode: ret = "sw\t%z1,%0"; break;
case SImode: ret = "sw\t%z1,%0"; break;
case HImode: ret = "sh\t%z1,%0"; break;
{
switch (mode)
{
+ default: break;
case SFmode: ret = "sw\t%.,%0"; break;
case SImode: ret = "sw\t%.,%0"; break;
case HImode: ret = "sh\t%.,%0"; break;
else
{
- operands[2] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (op1));
- operands[3] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (op1));
+ operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
+ operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
ret = "li\t%M0,%3\n\tli\t%L0,%2";
}
}
else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
{
- operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) >= 0 ? 0 : -1);
+ operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
ret = "li\t%M0,%2\n\tli\t%L0,%1";
}
{
switch (GET_CODE (addr))
{
+ default:
+ break;
+
case LO_SUM:
case HIGH:
return 1;
switch (GET_CODE (plus1))
{
+ default:
+ break;
+
case CONST_INT:
{
int value = INTVAL (plus1);
}
\f
+/* Make normal rtx_code into something we can index from an array */
+
+static enum internal_test
+map_test_to_internal_test (test_code)
+ enum rtx_code test_code;
+{
+ enum internal_test test = ITEST_MAX;
+
+ switch (test_code)
+ {
+ default: break;
+ case EQ: test = ITEST_EQ; break;
+ case NE: test = ITEST_NE; break;
+ case GT: test = ITEST_GT; break;
+ case GE: test = ITEST_GE; break;
+ case LT: test = ITEST_LT; break;
+ case LE: test = ITEST_LE; break;
+ case GTU: test = ITEST_GTU; break;
+ case GEU: test = ITEST_GEU; break;
+ case LTU: test = ITEST_LTU; break;
+ case LEU: test = ITEST_LEU; break;
+ }
+
+ return test;
+}
+
+\f
+/* Generate the code to compare two integer values. The return value is:
+ (reg:SI xx) The pseudo register the comparison is in
+ (rtx)0 No register, generate a simple branch. */
+
+rtx
+gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
+ enum rtx_code test_code; /* relational test (EQ, etc) */
+ rtx result; /* result to store comp. or 0 if branch */
+ rtx cmp0; /* first operand to compare */
+ rtx cmp1; /* second operand to compare */
+ int *p_invert; /* NULL or ptr to hold whether branch needs */
+ /* to reverse its test */
+{
+ struct cmp_info {
+ enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */
+ int const_low; /* low bound of constant we can accept */
+ int const_high; /* high bound of constant we can accept */
+ int const_add; /* constant to add (convert LE -> LT) */
+ int reverse_regs; /* reverse registers in test */
+ int invert_const; /* != 0 if invert value if cmp1 is constant */
+ int invert_reg; /* != 0 if invert value if cmp1 is register */
+ int unsignedp; /* != 0 for unsigned comparisons. */
+ };
+
+ static struct cmp_info info[ (int)ITEST_MAX ] = {
+
+ { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */
+ { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */
+ { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */
+ { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */
+ { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */
+ { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */
+ { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */
+ { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */
+ { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */
+ { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */
+ };
+
+ enum internal_test test;
+ struct cmp_info *p_info;
+ int branch_p;
+ int eqne_p;
+ int invert;
+ rtx reg;
+ rtx reg2;
+
+ test = map_test_to_internal_test (test_code);
+ if (test == ITEST_MAX)
+ abort ();
+
+ p_info = &info[ (int)test ];
+ eqne_p = (p_info->test_code == XOR);
+
+ /* Eliminate simple branches */
+ branch_p = (result == (rtx)0);
+ if (branch_p)
+ {
+ if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
+ {
+ /* Comparisons against zero are simple branches */
+ if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+ return (rtx)0;
+
+ /* Test for beq/bne. */
+ if (eqne_p)
+ return (rtx)0;
+ }
+
+ /* allocate a pseudo to calculate the value in. */
+ result = gen_reg_rtx (SImode);
+ }
+
+ /* Make sure we can handle any constants given to us. */
+ if (GET_CODE (cmp0) == CONST_INT)
+ cmp0 = force_reg (SImode, cmp0);
+
+ if (GET_CODE (cmp1) == CONST_INT)
+ {
+ HOST_WIDE_INT value = INTVAL (cmp1);
+ if (value < p_info->const_low || value > p_info->const_high)
+ cmp1 = force_reg (SImode, cmp1);
+ }
+
+ /* See if we need to invert the result. */
+ invert = (GET_CODE (cmp1) == CONST_INT)
+ ? p_info->invert_const
+ : p_info->invert_reg;
+
+ if (p_invert != (int *)0)
+ {
+ *p_invert = invert;
+ invert = FALSE;
+ }
+
+ /* Comparison to constants, may involve adding 1 to change a LT into LE.
+ Comparison between two registers, may involve switching operands. */
+ if (GET_CODE (cmp1) == CONST_INT)
+ {
+ if (p_info->const_add != 0)
+ {
+ HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
+ /* If modification of cmp1 caused overflow,
+ we would get the wrong answer if we follow the usual path;
+ thus, x > 0xffffffffu would turn into x > 0u. */
+ if ((p_info->unsignedp
+ ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1)
+ : new > INTVAL (cmp1))
+ != (p_info->const_add > 0))
+ /* 1 is the right value in the LE and LEU case.
+ In the GT and GTU case, *p_invert is already set,
+ so this is effectively 0. */
+ return force_reg (SImode, const1_rtx);
+ else
+ cmp1 = GEN_INT (new);
+ }
+ }
+ else if (p_info->reverse_regs)
+ {
+ rtx temp = cmp0;
+ cmp0 = cmp1;
+ cmp1 = temp;
+ }
+
+ if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+ reg = cmp0;
+ else
+ {
+ reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result;
+ emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1));
+ }
+
+ if (test == ITEST_NE)
+ {
+ emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx));
+ invert = FALSE;
+ }
+
+ else if (test == ITEST_EQ)
+ {
+ reg2 = (invert) ? gen_reg_rtx (SImode) : result;
+ emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx));
+ reg = reg2;
+ }
+
+ if (invert)
+ emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx));
+
+ return result;
+}
+
+\f
/* Emit the common code for doing conditional branches.
operand[0] is the label to jump to.
The comparison operands are saved away by cmp{si,sf,df}. */
rtx operands[];
enum rtx_code test_code;
{
- enum {
- I_EQ,
- I_NE,
- I_GT,
- I_GE,
- I_LT,
- I_LE,
- I_GTU,
- I_GEU,
- I_LTU,
- I_LEU,
- I_MAX
- } test = I_MAX;
-
- static enum machine_mode mode_map[(int)CMP_MAX][(int)I_MAX] = {
+ static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = {
{ /* CMP_SI */
- CC_EQmode, /* eq */
- CC_EQmode, /* ne */
- CCmode, /* gt */
- CCmode, /* ge */
- CCmode, /* lt */
- CCmode, /* le */
- CCmode, /* gtu */
- CCmode, /* geu */
- CCmode, /* ltu */
- CCmode, /* leu */
+ SImode, /* eq */
+ SImode, /* ne */
+ SImode, /* gt */
+ SImode, /* ge */
+ SImode, /* lt */
+ SImode, /* le */
+ SImode, /* gtu */
+ SImode, /* geu */
+ SImode, /* ltu */
+ SImode, /* leu */
},
{ /* CMP_SF */
CC_FPmode, /* eq */
- CC_FPmode, /* ne */
+ CC_REV_FPmode, /* ne */
CC_FPmode, /* gt */
CC_FPmode, /* ge */
CC_FPmode, /* lt */
},
{ /* CMP_DF */
CC_FPmode, /* eq */
- CC_FPmode, /* ne */
+ CC_REV_FPmode, /* ne */
CC_FPmode, /* gt */
CC_FPmode, /* ge */
CC_FPmode, /* lt */
};
enum machine_mode mode;
- enum cmp_type type = branch_type;
- rtx cmp0 = branch_cmp[0];
- rtx cmp1 = branch_cmp[1];
- rtx label = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
-
- /* Make normal rtx_code into something we can index from an array */
- switch (test_code)
- {
- case EQ: test = I_EQ; break;
- case NE: test = I_NE; break;
- case GT: test = I_GT; break;
- case GE: test = I_GE; break;
- case LT: test = I_LT; break;
- case LE: test = I_LE; break;
- case GTU: test = I_GTU; break;
- case GEU: test = I_GEU; break;
- case LTU: test = I_LTU; break;
- case LEU: test = I_LEU; break;
- }
-
- if (test == I_MAX)
+ enum cmp_type type = branch_type;
+ rtx cmp0 = branch_cmp[0];
+ rtx cmp1 = branch_cmp[1];
+ rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
+ rtx label2 = pc_rtx;
+ rtx reg = (rtx)0;
+ int invert = 0;
+ enum internal_test test = map_test_to_internal_test (test_code);
+
+ if (test == ITEST_MAX)
{
- mode = CCmode;
+ mode = SImode;
goto fail;
}
- /* Get the machine mode to use (CCmode, CC_EQmode, or CC_FPmode). */
+ /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */
mode = mode_map[(int)type][(int)test];
if (mode == VOIDmode)
goto fail;
goto fail;
case CMP_SI:
- /* Change >, >=, <, <= tests against 0 to use CC_0mode, since we have
- special instructions to do these tests directly. */
-
- if (mode == CCmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
- {
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp0));
- mode = CC_0mode;
- }
-
- else if (mode == CCmode && GET_CODE (cmp0) == CONST_INT && INTVAL (cmp0) == 0)
+ reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
+ if (reg != (rtx)0)
{
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp1));
- test_code = reverse_condition (test_code);
- mode = CC_0mode;
+ cmp0 = reg;
+ cmp1 = const0_rtx;
+ test_code = NE;
}
- else
- {
- /* force args to register for equality comparisons. */
- if (mode == CC_EQmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
- cmp1 = force_reg (SImode, cmp1);
+ /* Make sure not non-zero constant if ==/!= */
+ else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
+ cmp1 = force_reg (SImode, cmp1);
- emit_insn (gen_rtx (SET, VOIDmode,
- cc0_rtx,
- gen_rtx (COMPARE, mode, cmp0, cmp1)));
- }
break;
case CMP_DF:
- emit_insn (gen_cmpdf_internal (cmp0, cmp1));
- break;
-
case CMP_SF:
- emit_insn (gen_cmpsf_internal (cmp0, cmp1));
+ {
+ rtx reg = gen_rtx (REG, mode, FPSW_REGNUM);
+ emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1)));
+ cmp0 = reg;
+ cmp1 = const0_rtx;
+ test_code = NE;
+ }
break;
}
-
+
/* Generate the jump */
+ if (invert)
+ {
+ label2 = label1;
+ label1 = pc_rtx;
+ }
+
emit_jump_insn (gen_rtx (SET, VOIDmode,
pc_rtx,
gen_rtx (IF_THEN_ELSE, VOIDmode,
- gen_rtx (test_code, mode, cc0_rtx, const0_rtx),
- label,
- pc_rtx)));
+ gen_rtx (test_code, mode, cmp0, cmp1),
+ label1,
+ label2)));
+
return;
fail:
/* Internal code to generate the load and store of one word/short/byte.
The load is emitted directly, and the store insn is returned. */
+#if 0
static rtx
-block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align)
- rtx src_reg; /* register holding source memory addresss */
- rtx dest_reg; /* register holding dest. memory addresss */
+block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
+ rtx src_reg; /* register holding source memory address */
+ rtx dest_reg; /* register holding dest. memory address */
int *p_bytes; /* pointer to # bytes remaining */
int *p_offset; /* pointer to current offset */
int align; /* alignment */
+ rtx orig_src; /* original source for making a reg note */
{
int bytes; /* # bytes remaining */
int offset; /* offset to use */
rtx reg; /* temporary register */
rtx src_addr; /* source address */
rtx dest_addr; /* destination address */
+ rtx insn; /* insn of the load */
+ rtx orig_src_addr; /* original source address */
rtx (*load_func)(); /* function to generate load insn */
rtx (*store_func)(); /* function to generate destination insn */
store_func = gen_movsi;
}
+#if 0
+ /* Don't generate unaligned moves here, rather defer those to the
+ general movestrsi_internal pattern. */
else if (bytes >= UNITS_PER_WORD)
{
mode = SImode;
load_func = gen_movsi_ulw;
store_func = gen_movsi_usw;
}
+#endif
else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
{
}
else
{
- src_addr = gen_rtx (PLUS, Pmode, src_reg, gen_rtx (CONST_INT, VOIDmode, offset));
- dest_addr = gen_rtx (PLUS, Pmode, dest_reg, gen_rtx (CONST_INT, VOIDmode, offset));
+ src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset));
+ dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
}
reg = gen_reg_rtx (mode);
- emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
+ insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
+ orig_src_addr = XEXP (orig_src, 0);
+ if (CONSTANT_P (orig_src_addr))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV,
+ plus_constant (orig_src_addr, offset),
+ REG_NOTES (insn));
+
return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
}
+#endif
\f
/* Write a series of loads/stores to move some bytes. Generate load/stores as follows:
two temp registers are needed. Two delay slots are used
in deference to the R4000. */
+#if 0
static void
-block_move_sequence (dest_reg, src_reg, bytes, align)
+block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
rtx dest_reg; /* register holding destination address */
rtx src_reg; /* register holding source address */
int bytes; /* # bytes to move */
int align; /* max alignment to assume */
+ rtx orig_src; /* original source for making a reg note */
{
int offset = 0;
rtx prev2_store = (rtx)0;
prev2_store = prev_store;
prev_store = cur_store;
- cur_store = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align);
+ cur_store = block_move_load_store (dest_reg, src_reg,
+ &bytes, &offset,
+ align, orig_src);
}
/* Finish up last three stores. */
if (cur_store)
emit_insn (cur_store);
}
+#endif
\f
/* Write a loop to move a constant number of bytes. Generate load/stores as follows:
temp2 = src[1];
...
temp<last> = src[MAX_MOVE_REGS-1];
- src += MAX_MOVE_REGS;
dest[0] = temp1;
dest[1] = temp2;
...
dest[MAX_MOVE_REGS-1] = temp<last>;
+ src += MAX_MOVE_REGS;
dest += MAX_MOVE_REGS;
} while (src != final);
#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
static void
-block_move_loop (dest_reg, src_reg, bytes, align)
+block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
rtx dest_reg; /* register holding destination address */
rtx src_reg; /* register holding source address */
int bytes; /* # bytes to move */
int align; /* alignment */
+ rtx orig_src; /* original source for making a reg note */
{
- rtx stores[MAX_MOVE_REGS];
+ rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg);
+ rtx src_mem = gen_rtx (MEM, BLKmode, src_reg);
+ rtx align_rtx = GEN_INT (align);
rtx label;
rtx final_src;
rtx bytes_rtx;
- int i;
int leftover;
- int offset;
if (bytes < 2*MAX_MOVE_BYTES)
abort ();
label = gen_label_rtx ();
final_src = gen_reg_rtx (Pmode);
- bytes_rtx = gen_rtx (CONST_INT, VOIDmode, bytes);
+ bytes_rtx = GEN_INT (bytes);
if (bytes > 0x7fff)
{
emit_label (label);
- offset = 0;
- for (i = 0; i < MAX_MOVE_REGS; i++)
- stores[i] = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align);
-
- emit_insn (gen_addsi3 (src_reg, src_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES)));
- for (i = 0; i < MAX_MOVE_REGS; i++)
- emit_insn (stores[i]);
-
- emit_insn (gen_addsi3 (dest_reg, dest_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES)));
+ bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
+ emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
+ emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
+ emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
emit_insn (gen_cmpsi (src_reg, final_src));
emit_jump_insn (gen_bne (label));
if (leftover)
- block_move_sequence (dest_reg, src_reg, leftover, align);
+ emit_insn (gen_movstrsi_internal (dest_mem, src_mem,
+ GEN_INT (leftover),
+ align_rtx));
}
\f
rtx operands[];
{
rtx bytes_rtx = operands[2];
+ rtx align_rtx = operands[3];
int constp = (GET_CODE (bytes_rtx) == CONST_INT);
int bytes = (constp ? INTVAL (bytes_rtx) : 0);
- int align = INTVAL (operands[3]);
+ int align = INTVAL (align_rtx);
+ rtx orig_src = operands[1];
rtx src_reg;
rtx dest_reg;
if (constp && bytes <= 0)
return;
+ if (align > UNITS_PER_WORD)
+ align = UNITS_PER_WORD;
+
/* Move the address into scratch registers. */
dest_reg = copy_addr_to_reg (XEXP (operands[0], 0));
- src_reg = copy_addr_to_reg (XEXP (operands[1], 0));
+ src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
if (TARGET_MEMCPY)
block_move_call (dest_reg, src_reg, bytes_rtx);
+#if 0
+ else if (constp && bytes <= 3*align)
+ block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);
+#endif
+
else if (constp && bytes <= 2*MAX_MOVE_BYTES)
- block_move_sequence (dest_reg, src_reg, bytes, align);
+ emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
+ gen_rtx (MEM, BLKmode, src_reg),
+ bytes_rtx, align_rtx));
else if (constp && align >= UNITS_PER_WORD && optimize)
- block_move_loop (dest_reg, src_reg, bytes, align);
+ block_move_loop (dest_reg, src_reg, bytes, align, orig_src);
else if (constp && optimize)
{
bytes -= leftover;
emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
- emit_insn (gen_andsi3 (temp, temp, gen_rtx (CONST_INT, VOIDmode, UNITS_PER_WORD-1)));
+ emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
emit_insn (gen_cmpsi (temp, const0_rtx));
emit_jump_insn (gen_beq (aligned_label));
/* Unaligned loop. */
- block_move_loop (dest_reg, src_reg, bytes, 1);
+ block_move_loop (dest_reg, src_reg, bytes, 1, orig_src);
emit_jump_insn (gen_jump (join_label));
emit_barrier ();
/* Aligned loop. */
emit_label (aligned_label);
- block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD);
+ block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src);
emit_label (join_label);
/* Bytes at the end of the loop. */
if (leftover)
- block_move_sequence (dest_reg, src_reg, leftover, align);
+ {
+#if 0
+ if (leftover <= 3*align)
+ block_move_sequence (dest_reg, src_reg, leftover, align, orig_src);
+
+ else
+#endif
+ emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
+ gen_rtx (MEM, BLKmode, src_reg),
+ GEN_INT (leftover),
+ GEN_INT (align)));
+ }
}
else
}
\f
-/* Argument support functions. */
+/* Emit load/stores for a small constant block_move.
-/* Initialize CUMULATIVE_ARGS for a function. */
+ operands[0] is the memory address of the destination.
+ operands[1] is the memory address of the source.
+ operands[2] is the number of bytes to move.
+ operands[3] is the alignment.
+ operands[4] is a temp register.
+ operands[5] is a temp register.
+ ...
+ operands[3+num_regs] is the last temp register.
-void
-init_cumulative_args (cum, fntype, libname)
- CUMULATIVE_ARGS *cum; /* argument info to initialize */
- tree fntype; /* tree ptr for function decl */
- rtx libname; /* SYMBOL_REF of library name or 0 */
+ The block move type can be one of the following:
+ BLOCK_MOVE_NORMAL Do all of the block move.
+ BLOCK_MOVE_NOT_LAST Do all but the last store.
+ BLOCK_MOVE_LAST Do just the last store. */
+
+char *
+output_block_move (insn, operands, num_regs, move_type)
+ rtx insn;
+ rtx operands[];
+ int num_regs;
+ enum block_move_type move_type;
{
- tree param, next_param;
+ rtx dest_reg = XEXP (operands[0], 0);
+ rtx src_reg = XEXP (operands[1], 0);
+ int bytes = INTVAL (operands[2]);
+ int align = INTVAL (operands[3]);
+ int num = 0;
+ int offset = 0;
+ int use_lwl_lwr = FALSE;
+ int last_operand = num_regs+4;
+ int i;
+ rtx xoperands[10];
+
+ struct {
+ char *load; /* load insn without nop */
+ char *load_nop; /* load insn with trailing nop */
+ char *store; /* store insn */
+ char *final; /* if last_store used: NULL or swr */
+ char *last_store; /* last store instruction */
+ int offset; /* current offset */
+ enum machine_mode mode; /* mode to use on (MEM) */
+ } load_store[4];
+
+ /* Detect a bug in GCC, where it can give us a register
+ the same as one of the addressing registers. */
+ for (i = 4; i < last_operand; i++)
+ {
+ if (reg_mentioned_p (operands[i], operands[0])
+ || reg_mentioned_p (operands[i], operands[1]))
+ {
+ abort_with_insn (insn, "register passed as address and temp register to block move");
+ }
+ }
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "\ninit_cumulative_args\n");
+ /* If we are given global or static addresses, and we would be
+ emitting a few instructions, try to save time by using a
+ temporary register for the pointer. */
+ if (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL)
+ {
+ if (CONSTANT_P (src_reg))
+ {
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[1], 1);
- cum->gp_reg_found = 0;
- cum->arg_number = 0;
- cum->arg_words = 0;
+ src_reg = operands[ 3 + num_regs-- ];
+ if (move_type != BLOCK_MOVE_LAST)
+ {
+ xoperands[1] = operands[1];
+ xoperands[0] = src_reg;
+ output_asm_insn ("la\t%0,%1", xoperands);
+ }
+ }
- /* Determine if this function has variable arguments. This is
- indicated by the last argument being 'void_type_mode' if there
- are no variable arguments. The standard MIPS calling sequence
- passes all arguments in the general purpose registers in this
- case. */
+ if (CONSTANT_P (dest_reg))
+ {
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[0], 1);
- for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
- param != (tree)0;
- param = next_param)
- {
- next_param = TREE_CHAIN (param);
- if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
- cum->gp_reg_found = 1;
+ dest_reg = operands[ 3 + num_regs-- ];
+ if (move_type != BLOCK_MOVE_LAST)
+ {
+ xoperands[1] = operands[0];
+ xoperands[0] = dest_reg;
+ output_asm_insn ("la\t%0,%1", xoperands);
+ }
+ }
}
- /* Determine if the function is returning a structure, if so,
- advance by one argument. */
+ if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
+ num_regs = (sizeof (load_store) / sizeof (load_store[0]));
- if (fntype
- && (TREE_CODE (fntype) == FUNCTION_TYPE || TREE_CODE (fntype) == METHOD_TYPE)
- && TREE_TYPE (fntype) != 0)
+ else if (num_regs < 1)
+ abort ();
+
+ if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0)
+ output_asm_insn (".set\tnoreorder", operands);
+
+ while (bytes > 0)
{
- tree ret_type = TREE_TYPE (fntype);
- enum tree_code ret_code = TREE_CODE (ret_type);
+ load_store[num].offset = offset;
- if (ret_code == RECORD_TYPE || ret_code == UNION_TYPE)
+ if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
{
- cum->gp_reg_found = 1;
- cum->arg_number = 1;
- cum->arg_words = 1;
+ load_store[num].load = "lw\t%0,%1";
+ load_store[num].load_nop = "lw\t%0,%1%#";
+ load_store[num].store = "sw\t%0,%1";
+ load_store[num].last_store = "sw\t%0,%1";
+ load_store[num].final = (char *)0;
+ load_store[num].mode = SImode;
+ offset += UNITS_PER_WORD;
+ bytes -= UNITS_PER_WORD;
}
- }
-}
-/* Advance the argument to the next argument position. */
+ else if (bytes >= UNITS_PER_WORD)
+ {
+#if BYTES_BIG_ENDIAN
+ load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
+ load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
+ load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
+ load_store[num].last_store = "swr\t%0,%2";
+ load_store[num].final = "swl\t%0,%1";
+#else
+ load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
+ load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
+ load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
+ load_store[num].last_store = "swr\t%0,%1";
+ load_store[num].final = "swl\t%0,%2";
+#endif
+ load_store[num].mode = SImode;
+ offset += UNITS_PER_WORD;
+ bytes -= UNITS_PER_WORD;
+ use_lwl_lwr = TRUE;
+ }
-void
-function_arg_advance (cum, mode, type, named)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
-{
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr,
- "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n",
- cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
- type, named);
+ else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
+ {
+ load_store[num].load = "lh\t%0,%1";
+ load_store[num].load_nop = "lh\t%0,%1%#";
+ load_store[num].store = "sh\t%0,%1";
+ load_store[num].last_store = "sh\t%0,%1";
+ load_store[num].final = (char *)0;
+ load_store[num].offset = offset;
+ load_store[num].mode = HImode;
+ offset += UNITS_PER_SHORT;
+ bytes -= UNITS_PER_SHORT;
+ }
- cum->arg_number++;
- switch (mode)
- {
- default:
- error ("Illegal mode given to function_arg_advance");
- break;
+ else
+ {
+ load_store[num].load = "lb\t%0,%1";
+ load_store[num].load_nop = "lb\t%0,%1%#";
+ load_store[num].store = "sb\t%0,%1";
+ load_store[num].last_store = "sb\t%0,%1";
+ load_store[num].final = (char *)0;
+ load_store[num].mode = QImode;
+ offset++;
+ bytes--;
+ }
+
+ if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
+ {
+ dslots_load_total++;
+ dslots_load_filled++;
+
+ if (CONSTANT_P (src_reg))
+ mips_count_memory_refs (src_reg, 1);
+
+ if (CONSTANT_P (dest_reg))
+ mips_count_memory_refs (dest_reg, 1);
+ }
+
+ /* Emit load/stores now if we have run out of registers or are
+ at the end of the move. */
+
+ if (++num == num_regs || bytes == 0)
+ {
+ /* If only load/store, we need a NOP after the load. */
+ if (num == 1)
+ {
+ load_store[0].load = load_store[0].load_nop;
+ if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
+ dslots_load_filled--;
+ }
+
+ if (move_type != BLOCK_MOVE_LAST)
+ {
+ for (i = 0; i < num; i++)
+ {
+ int offset;
+
+ if (!operands[i+4])
+ abort ();
+
+ if (GET_MODE (operands[i+4]) != load_store[i].mode)
+ operands[i+4] = gen_rtx (REG, load_store[i].mode, REGNO (operands[i+4]));
+
+ offset = load_store[i].offset;
+ xoperands[0] = operands[i+4];
+ xoperands[1] = gen_rtx (MEM, load_store[i].mode,
+ plus_constant (src_reg, offset));
+
+ if (use_lwl_lwr)
+ xoperands[2] = gen_rtx (MEM, load_store[i].mode,
+ plus_constant (src_reg, UNITS_PER_WORD-1+offset));
+
+ output_asm_insn (load_store[i].load, xoperands);
+ }
+ }
+
+ for (i = 0; i < num; i++)
+ {
+ int last_p = (i == num-1 && bytes == 0);
+ int offset = load_store[i].offset;
+
+ xoperands[0] = operands[i+4];
+ xoperands[1] = gen_rtx (MEM, load_store[i].mode,
+ plus_constant (dest_reg, offset));
+
+
+ if (use_lwl_lwr)
+ xoperands[2] = gen_rtx (MEM, load_store[i].mode,
+ plus_constant (dest_reg, UNITS_PER_WORD-1+offset));
+
+ if (move_type == BLOCK_MOVE_NORMAL)
+ output_asm_insn (load_store[i].store, xoperands);
+
+ else if (move_type == BLOCK_MOVE_NOT_LAST)
+ {
+ if (!last_p)
+ output_asm_insn (load_store[i].store, xoperands);
+
+ else if (load_store[i].final != (char *)0)
+ output_asm_insn (load_store[i].final, xoperands);
+ }
+
+ else if (last_p)
+ output_asm_insn (load_store[i].last_store, xoperands);
+ }
+
+ num = 0; /* reset load_store */
+ use_lwl_lwr = FALSE; /* reset whether or not we used lwl/lwr */
+ }
+ }
+
+ if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0)
+ output_asm_insn (".set\treorder", operands);
+
+ return "";
+}
+
+\f
+/* Argument support functions. */
+
+/* Initialize CUMULATIVE_ARGS for a function. */
+
+void
+init_cumulative_args (cum, fntype, libname)
+ CUMULATIVE_ARGS *cum; /* argument info to initialize */
+ tree fntype; /* tree ptr for function decl */
+ rtx libname; /* SYMBOL_REF of library name or 0 */
+{
+ static CUMULATIVE_ARGS zero_cum;
+ tree param, next_param;
+
+ if (TARGET_DEBUG_E_MODE)
+ {
+ fprintf (stderr, "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
+ if (!fntype)
+ fputc ('\n', stderr);
+
+ else
+ {
+ tree ret_type = TREE_TYPE (fntype);
+ fprintf (stderr, ", fntype code = %s, ret code = %s\n",
+ tree_code_name[ (int)TREE_CODE (fntype) ],
+ tree_code_name[ (int)TREE_CODE (ret_type) ]);
+ }
+ }
+
+ *cum = zero_cum;
+
+ /* Determine if this function has variable arguments. This is
+ indicated by the last argument being 'void_type_mode' if there
+ are no variable arguments. The standard MIPS calling sequence
+ passes all arguments in the general purpose registers in this
+ case. */
+
+ for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
+ param != (tree)0;
+ param = next_param)
+ {
+ next_param = TREE_CHAIN (param);
+ if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
+ cum->gp_reg_found = 1;
+ }
+}
+
+/* Advance the argument to the next argument position. */
+
+void
+function_arg_advance (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int named; /* whether or not the argument was named */
+{
+ if (TARGET_DEBUG_E_MODE)
+ fprintf (stderr,
+ "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n",
+ cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
+ type, named);
+
+ cum->arg_number++;
+ switch (mode)
+ {
+ default:
+ error ("Illegal mode given to function_arg_advance");
+ break;
case VOIDmode:
break;
tree type; /* type of the argument or 0 if lib support */
int named; /* != 0 for normal args, == 0 for ... args */
{
+ rtx ret;
int regbase = -1;
int bias = 0;
+ int struct_p = ((type != (tree)0)
+ && (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE));
if (TARGET_DEBUG_E_MODE)
fprintf (stderr,
: FP_ARG_FIRST;
break;
- case VOIDmode:
case BLKmode:
+ if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD)
+ cum->arg_words += (cum->arg_words & 1);
+
+ regbase = GP_ARG_FIRST;
+ break;
+
+ case VOIDmode:
case QImode:
case HImode:
case SImode:
if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
{
if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "<stack>\n");
+ fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
- return 0;
+ ret = (rtx)0;
}
+ else
+ {
+ if (regbase == -1)
+ abort ();
- if (regbase == -1)
- abort ();
+ ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "%s\n", reg_names[regbase + cum->arg_words + bias]);
+ if (TARGET_DEBUG_E_MODE)
+ fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
+ struct_p ? ", [struct]" : "");
+
+ /* The following is a hack in order to pass 1 byte structures
+ the same way that the MIPS compiler does (namely by passing
+ the structure in the high byte or half word of the register).
+ This also makes varargs work. If we have such a structure,
+ we save the adjustment RTL, and the call define expands will
+ emit them. For the VOIDmode argument (argument after the
+ last real argument, pass back a parallel vector holding each
+ of the adjustments. */
+
+ if (struct_p && (mode == QImode || mode == HImode))
+ {
+ rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode));
+ rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
+ cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
+ }
+ }
+
+ if (mode == VOIDmode && cum->num_adjusts > 0)
+ ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust));
- return gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+ return ret;
}
\f
#ifdef SIGINFO
-#include <sys/wait.h>
-
static void
siginfo (signo)
int signo;
{
- char print_pid[50];
- char *argv[4];
- pid_t pid;
- int status;
-
fprintf (stderr, "compiling '%s' in '%s'\n",
(current_function_name != (char *)0) ? current_function_name : "<toplevel>",
(current_function_file != (char *)0) ? current_function_file : "<no file>");
-
- /* Spawn a ps to tell about current memory usage, etc. */
- sprintf (print_pid, "-p%d,%d", getpid (), getppid ());
- argv[0] = "ps";
- argv[1] = print_pid;
- argv[2] = "-ouser,state,pid,pri,nice,wchan,tt,start,usertime,systime,pcpu,cp,inblock,oublock,vsize,rss,pmem,ucomm";
- argv[3] = (char *)0;
-
- pid = vfork ();
- if (pid == 0) /* child context */
- {
- execv ("/usr/bin/ps", argv);
- execv ("/usr/sbin/ps", argv);
- execvp ("ps", argv);
- perror ("ps");
- _exit (1);
- }
-
- else if (pid > 0) /* parent context */
- {
- void (*sigint)(int) = signal (SIGINT, SIG_IGN);
- void (*sigquit)(int) = signal (SIGQUIT, SIG_IGN);
-
- (void) waitpid (pid, &status, 0);
-
- (void) signal (SIGINT, sigint);
- (void) signal (SIGQUIT, sigquit);
- }
-
- signal (SIGINFO, siginfo);
+ fflush (stderr);
}
-
#endif /* SIGINFO */
\f
}
#endif
+#if defined(_IOLBF)
+#if defined(ultrix) || defined(__ultrix) || defined(__OSF1__) || defined(__osf__) || defined(osf)
+ /* If -mstats and -quiet, make stderr line buffered. */
+ if (quiet_flag && TARGET_STATS)
+ setvbuf (stderr, (char *)0, _IOLBF, BUFSIZ);
+#endif
+#endif
+
/* Set up the classification arrays now. */
mips_rtx_classify[(int)PLUS] = CLASS_ADD_OP;
mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP;
mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
mips_char_to_class['h'] = HI_REG;
mips_char_to_class['l'] = LO_REG;
- mips_char_to_class['s'] = ST_REGS;
mips_char_to_class['x'] = MD_REGS;
mips_char_to_class['y'] = GR_REGS;
+ mips_char_to_class['z'] = ST_REGS;
/* Set up array to map GCC register number to debug register number.
Ignore the special purpose register numbers. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
- register int temp = FALSE;
+ register int temp;
+
+ if (mode == CC_FPmode || mode == CC_REV_FPmode)
+ temp = (regno == FPSW_REGNUM);
- if (GP_REG_P (regno))
+ else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
else if (FP_REG_P (regno))
temp = ((TARGET_FLOAT64 || ((regno & 1) == 0))
- && (TARGET_DEBUG_H_MODE
- || class == MODE_FLOAT
- || class == MODE_COMPLEX_FLOAT));
+ && (class == MODE_FLOAT
+ || class == MODE_COMPLEX_FLOAT
+ || (TARGET_DEBUG_H_MODE && class == MODE_INT)));
else if (MD_REG_P (regno))
temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode));
- else if (ST_REG_P (regno))
- temp = ((mode == SImode) || (class == MODE_CC));
+ else
+ temp = FALSE;
mips_hard_regno_mode_ok[(int)mode][regno] = temp;
}
\f
/*
- * If the frame pointer has been eliminated, the offset for an auto
- * or argument will be based on the stack pointer. But this is not
- * what the debugger expects--it needs to find an offset off of the
- * frame pointer (whether it exists or not). So here we turn all
- * offsets into those based on the (possibly virtual) frame pointer.
+ * The MIPS debug format wants all automatic variables and arguments
+ * to be in terms of the virtual frame pointer (stack pointer before
+ * any adjustment in the function), while the MIPS 3.0 linker wants
+ * the frame pointer to be the stack pointer after the initial
+ * adjustment. So, we do the adjustment here. The arg pointer (which
+ * is eliminated) points to the virtual frame pointer, while the frame
+ * pointer (which may be eliminated) points to the stack pointer after
+ * the initial adjustments.
*/
int
if (!offset)
offset = INTVAL (offset2);
- if (reg == stack_pointer_rtx)
+ if (reg == stack_pointer_rtx || reg == frame_pointer_rtx)
{
int frame_size = (!current_frame_info.initialized)
? compute_frame_size (get_frame_size ())
offset = offset - frame_size;
}
-
- /* Any other register is, we hope, either the frame pointer,
- or a pseudo equivalent to the frame pointer. (Assign_parms
- copies the arg pointer to a pseudo if ARG_POINTER_REGNUM is
- equal to FRAME_POINTER_REGNUM, so references off of the
- arg pointer are all off a pseudo.) Seems like all we can
- do is to just return OFFSET and hope for the best. */
+ /* sdbout_parms does not want this to crash for unrecognized cases. */
+#if 0
+ else if (reg != arg_pointer_rtx)
+ abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer.");
+#endif
return offset;
}
+
\f
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand X. X is an RTL
break;
case REG:
+ if (REGNO (addr) == ARG_POINTER_REGNUM)
+ abort_with_insn (addr, "Arg pointer not eliminated.");
+
fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
break;
if (!CONSTANT_P (offset))
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2");
+ if (REGNO (reg) == ARG_POINTER_REGNUM)
+ abort_with_insn (addr, "Arg pointer not eliminated.");
+
output_addr_const (file, offset);
fprintf (file, "(%s)", reg_names [REGNO (reg)]);
}
}
\f
+/* If defined, a C statement to be executed just prior to the
+ output of assembler code for INSN, to modify the extracted
+ operands so they will be output differently.
+
+ Here the argument OPVEC is the vector containing the operands
+ extracted from INSN, and NOPERANDS is the number of elements of
+ the vector which contain meaningful data for this insn. The
+ contents of this vector are what will be used to convert the
+ insn template into assembler code, so you can change the
+ assembler output by changing the contents of the vector.
+
+ We use it to check if the current insn needs a nop in front of it
+ because of load delays, and also to update the delay slot
+ statistics. */
+
+void
+final_prescan_insn (insn, opvec, noperands)
+ rtx insn;
+ rtx opvec[];
+ int noperands;
+{
+ if (dslots_number_nops > 0)
+ {
+ rtx pattern = PATTERN (insn);
+ int length = get_attr_length (insn);
+
+ /* Do we need to emit a NOP? */
+ if (length == 0
+ || (mips_load_reg != (rtx)0 && reg_mentioned_p (mips_load_reg, pattern))
+ || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern))
+ || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern))
+ || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern)))
+ fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+
+ else
+ dslots_load_filled++;
+
+ while (--dslots_number_nops > 0)
+ fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+
+ mips_load_reg = (rtx)0;
+ mips_load_reg2 = (rtx)0;
+ mips_load_reg3 = (rtx)0;
+ mips_load_reg4 = (rtx)0;
+
+ if (set_noreorder && --set_noreorder == 0)
+ fputs ("\t.set\treorder\n", asm_out_file);
+ }
+
+ if (TARGET_STATS)
+ {
+ enum rtx_code code = GET_CODE (insn);
+ if (code == JUMP_INSN || code == CALL_INSN)
+ dslots_jump_total++;
+ }
+}
+
+\f
/* Output at beginning of assembler file.
If we are optimizing to use the global pointer, create a temporary
file to hold all of the text stuff, and write it out to the end.
if (TARGET_ABICALLS)
fprintf (stream, "\t.abicalls\n");
- /* put gcc_compiled. in data, not text */
- data_section ();
-
if (TARGET_GP_OPT)
{
asm_out_data_file = stream;
struct extern_list *p;
int len;
+ if (HALF_PIC_P ())
+ HALF_PIC_FINISH (file);
+
if (TARGET_GP_OPT)
{
if (extern_head)
}
\f
+/* Output a double precision value to the assembler. If both the
+ host and target are IEEE, emit the values in hex. */
+
+void
+mips_output_double (stream, value)
+ FILE *stream;
+ REAL_VALUE_TYPE value;
+{
+#ifdef REAL_VALUE_TO_TARGET_DOUBLE
+ long value_long[2];
+ REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
+
+ fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n",
+ value_long[0], value, value_long[1]);
+#else
+ fprintf (stream, "\t.double\t%.20g\n", value);
+#endif
+}
+
+
+/* Output a single precision value to the assembler. If both the
+ host and target are IEEE, emit the values in hex. */
+
+void
+mips_output_float (stream, value)
+ FILE *stream;
+ REAL_VALUE_TYPE value;
+{
+#ifdef REAL_VALUE_TO_TARGET_SINGLE
+ long value_long;
+ REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
+
+ fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value);
+#else
+ fprintf (stream, "\t.float\t%.12g\n", value);
+#endif
+}
+
+\f
+/* Return TRUE if any register used in the epilogue is used. This to insure
+ any insn put into the epilogue delay slots is safe. */
+
+int
+epilogue_reg_mentioned_p (insn)
+ rtx insn;
+{
+ register char *fmt;
+ register int i;
+ register enum rtx_code code;
+ register int regno;
+
+ if (insn == (rtx)0)
+ return 0;
+
+ if (GET_CODE (insn) == LABEL_REF)
+ return 0;
+
+ code = GET_CODE (insn);
+ switch (code)
+ {
+ case REG:
+ regno = REGNO (insn);
+ if (regno == STACK_POINTER_REGNUM)
+ return 1;
+
+ if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)
+ return 1;
+
+ if (!call_used_regs[regno])
+ return 1;
+
+ if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM)
+ return 0;
+
+ if (!current_frame_info.initialized)
+ compute_frame_size (get_frame_size ());
+
+ return (current_frame_info.total_size >= 32768);
+
+ case SCRATCH:
+ case CC0:
+ case PC:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ return 0;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (insn, i) - 1; j >= 0; j--)
+ if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+\f
/* Return the bytes needed to compute the frame pointer from the current
stack pointer.
| GP save for V.4 abi |
| |
+-----------------------+
- | |
- | local variables |
- | |
- +-----------------------+
| |
| fp register save |
| |
| |
+-----------------------+
| |
+ | local variables |
+ | |
+ +-----------------------+
+ | |
| alloca allocations |
| |
+-----------------------+
*/
-unsigned long
+long
compute_frame_size (size)
int size; /* # of var. bytes allocated */
{
int regno;
- unsigned long total_size; /* # bytes that the entire frame takes up */
- unsigned long var_size; /* # bytes that variables take up */
- unsigned long args_size; /* # bytes that outgoing arguments take up */
- unsigned long extra_size; /* # extra bytes */
- unsigned int gp_reg_rounded; /* # bytes needed to store gp after rounding */
- unsigned int gp_reg_size; /* # bytes needed to store gp regs */
- unsigned int fp_reg_size; /* # bytes needed to store fp regs */
- unsigned long mask; /* mask of saved gp registers */
- unsigned long fmask; /* mask of saved fp registers */
- int fp_inc; /* 1 or 2 depending on the size of fp regs */
- int fp_bits; /* bitmask to use for each fp register */
-
- extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0)
- -STARTING_FRAME_OFFSET);
+ long total_size; /* # bytes that the entire frame takes up */
+ long var_size; /* # bytes that variables take up */
+ long args_size; /* # bytes that outgoing arguments take up */
+ long extra_size; /* # extra bytes */
+ long gp_reg_rounded; /* # bytes needed to store gp after rounding */
+ long gp_reg_size; /* # bytes needed to store gp regs */
+ long fp_reg_size; /* # bytes needed to store fp regs */
+ long mask; /* mask of saved gp registers */
+ long fmask; /* mask of saved fp registers */
+ int fp_inc; /* 1 or 2 depending on the size of fp regs */
+ long fp_bits; /* bitmask to use for each fp register */
- var_size = MIPS_STACK_ALIGN (size);
- args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
- total_size = var_size + args_size + extra_size;
gp_reg_size = 0;
fp_reg_size = 0;
mask = 0;
fmask = 0;
+ extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
+ var_size = MIPS_STACK_ALIGN (size);
+ args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
+
+ /* The MIPS 3.0 linker does not like functions that dynamically
+ allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
+ looks like we are trying to create a second frame pointer to the
+ function, so allocate some stack space to make it happy. */
+
+ if (args_size == 0 && current_function_calls_alloca)
+ args_size = 4*UNITS_PER_WORD;
+
+ total_size = var_size + args_size + extra_size;
/* Calculate space needed for gp registers. */
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
if (MUST_SAVE_REGISTER (regno))
{
gp_reg_size += UNITS_PER_WORD;
- mask |= 1 << (regno - GP_REG_FIRST);
+ mask |= 1L << (regno - GP_REG_FIRST);
}
}
current_frame_info.mask = mask;
current_frame_info.fmask = fmask;
current_frame_info.initialized = reload_completed;
+ current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
+ current_frame_info.num_fp = fp_reg_size / (2*UNITS_PER_WORD);
if (mask)
{
- unsigned long offset = args_size + gp_reg_size - UNITS_PER_WORD;
+ unsigned long offset = args_size + var_size + gp_reg_size - UNITS_PER_WORD;
current_frame_info.gp_sp_offset = offset;
current_frame_info.gp_save_offset = offset - total_size;
}
+ else
+ {
+ current_frame_info.gp_sp_offset = 0;
+ current_frame_info.gp_save_offset = 0;
+ }
+
if (fmask)
{
- unsigned long offset = args_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
+ unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
current_frame_info.fp_sp_offset = offset;
current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
}
+ else
+ {
+ current_frame_info.fp_sp_offset = 0;
+ current_frame_info.fp_save_offset = 0;
+ }
/* Ok, we're done. */
return total_size;
}
\f
-/* Common code to save/restore registers. */
+/* Common code to emit the insns (or to write the instructions to a file)
+ to save/restore registers.
-void
-save_restore (file, gp_op, gp_2word_op, fp_op)
- FILE *file; /* stream to write to */
- char *gp_op; /* operation to do on gp registers */
- char *gp_2word_op; /* 2 word op to do on gp registers */
- char *fp_op; /* operation to do on fp registers */
+ Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
+ is not modified within save_restore_insns. */
+
+#define BITSET_P(value,bit) (((value) & (1L << (bit))) != 0)
+
+static void
+save_restore_insns (store_p, large_reg, large_offset, file)
+ int store_p; /* true if this is prologue */
+ rtx large_reg; /* register holding large offset constant or NULL */
+ long large_offset; /* large constant offset value */
+ FILE *file; /* file to write instructions to instead of making RTL */
{
+ long mask = current_frame_info.mask;
+ long fmask = current_frame_info.fmask;
int regno;
- unsigned long mask = current_frame_info.mask;
- unsigned long fmask = current_frame_info.fmask;
- unsigned long gp_offset;
- unsigned long fp_offset;
- unsigned long max_offset;
- char *base_reg;
+ rtx base_reg_rtx;
+ long base_offset;
+ long gp_offset;
+ long fp_offset;
+ long end_offset;
+
+ if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
+ abort ();
if (mask == 0 && fmask == 0)
return;
- base_reg = reg_names[STACK_POINTER_REGNUM];
- gp_offset = current_frame_info.gp_sp_offset;
- fp_offset = current_frame_info.fp_sp_offset;
- max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset;
-
- /* Deal with calling functions with a large structure. */
- if (max_offset >= 32768)
- {
- char *temp = reg_names[MIPS_TEMP2_REGNUM];
- fprintf (file, "\tli\t%s,%ld\n", temp, max_offset);
- fprintf (file, "\taddu\t%s,%s,%s\n", temp, temp, base_reg);
- base_reg = temp;
- gp_offset = max_offset - gp_offset;
- fp_offset = max_offset - fp_offset;
- }
-
/* Save registers starting from high to low. The debuggers prefer
at least the return register be stored at func+4, and also it
allows us not to need a nop in the epilog if at least one
register is reloaded in addition to return address. */
- if (mask || frame_pointer_needed)
+ /* Save GP registers if needed. */
+ if (mask)
{
+ /* Pick which pointer to use as a base register. For small
+ frames, just use the stack pointer. Otherwise, use a
+ temporary register. Save 2 cycles if the save area is near
+ the end of a large frame, by reusing the constant created in
+ the prologue/epilogue to adjust the stack frame. */
+
+ gp_offset = current_frame_info.gp_sp_offset;
+ end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD);
+
+ if (gp_offset < 0 || end_offset < 0)
+ fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
+ gp_offset, end_offset);
+
+ else if (gp_offset < 32768)
+ {
+ base_reg_rtx = stack_pointer_rtx;
+ base_offset = 0;
+ }
+
+ else if (large_reg != (rtx)0
+ && (((unsigned long)(large_offset - gp_offset)) < 32768)
+ && (((unsigned long)(large_offset - end_offset)) < 32768))
+ {
+ base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+ base_offset = large_offset;
+ if (file == (FILE *)0)
+ emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ else
+ fprintf (file, "\taddu\t%s,%s,%s\n",
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[REGNO (large_reg)],
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+
+ else
+ {
+ base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+ base_offset = gp_offset;
+ if (file == (FILE *)0)
+ {
+ emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
+ emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ }
+ else
+ fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+ reg_names[MIPS_TEMP2_REGNUM],
+ (long)base_offset,
+ (long)base_offset,
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
{
- if ((mask & (1L << (regno - GP_REG_FIRST))) != 0
- || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed))
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
{
- fprintf (file, "\t%s\t%s,%d(%s)\n",
- gp_op, reg_names[regno],
- gp_offset, base_reg);
+ if (file == (FILE *)0)
+ {
+ rtx reg_rtx = gen_rtx (REG, Pmode, regno);
+ rtx mem_rtx = gen_rtx (MEM, Pmode,
+ gen_rtx (PLUS, Pmode, base_reg_rtx,
+ GEN_INT (gp_offset - base_offset)));
+
+ if (store_p)
+ emit_move_insn (mem_rtx, reg_rtx);
+ else
+ emit_move_insn (reg_rtx, mem_rtx);
+ }
+ else
+ fprintf (file, "\t%s\t%s,%ld(%s)\n",
+ (store_p) ? "sw" : "lw",
+ reg_names[regno],
+ gp_offset - base_offset,
+ reg_names[REGNO(base_reg_rtx)]);
gp_offset -= UNITS_PER_WORD;
}
}
}
+ else
+ {
+ base_reg_rtx = (rtx)0; /* Make sure these are initialzed */
+ base_offset = 0;
+ }
+ /* Save floating point registers if needed. */
if (fmask)
{
int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
+ /* Pick which pointer to use as a base register. */
+ fp_offset = current_frame_info.fp_sp_offset;
+ end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD);
+
+ if (fp_offset < 0 || end_offset < 0)
+ fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
+ fp_offset, end_offset);
+
+ else if (fp_offset < 32768)
+ {
+ base_reg_rtx = stack_pointer_rtx;
+ base_offset = 0;
+ }
+
+ else if (base_reg_rtx != (rtx)0
+ && (((unsigned long)(base_offset - fp_offset)) < 32768)
+ && (((unsigned long)(base_offset - end_offset)) < 32768))
+ {
+ ; /* already set up for gp registers above */
+ }
+
+ else if (large_reg != (rtx)0
+ && (((unsigned long)(large_offset - fp_offset)) < 32768)
+ && (((unsigned long)(large_offset - end_offset)) < 32768))
+ {
+ base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+ base_offset = large_offset;
+ if (file == (FILE *)0)
+ emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ else
+ fprintf (file, "\taddu\t%s,%s,%s\n",
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[REGNO (large_reg)],
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+
+ else
+ {
+ base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+ base_offset = fp_offset;
+ if (file == (FILE *)0)
+ {
+ emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
+ emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ }
+ else
+ fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+ reg_names[MIPS_TEMP2_REGNUM],
+ (long)base_offset,
+ (long)base_offset,
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[MIPS_TEMP2_REGNUM],
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+
for (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
{
- if ((fmask & (1L << (regno - FP_REG_FIRST))) != 0)
+ if (BITSET_P (fmask, regno - FP_REG_FIRST))
{
- fprintf (file, "\t%s\t%s,%d(%s)\n",
- fp_op, reg_names[regno], fp_offset, base_reg);
+ if (file == (FILE *)0)
+ {
+ rtx reg_rtx = gen_rtx (REG, DFmode, regno);
+ rtx mem_rtx = gen_rtx (MEM, DFmode,
+ gen_rtx (PLUS, Pmode, base_reg_rtx,
+ GEN_INT (fp_offset - base_offset)));
+
+ if (store_p)
+ emit_move_insn (mem_rtx, reg_rtx);
+ else
+ emit_move_insn (reg_rtx, mem_rtx);
+ }
+ else
+ fprintf (file, "\t%s\t%s,%ld(%s)\n",
+ (store_p) ? "s.d" : "l.d",
+ reg_names[regno],
+ fp_offset - base_offset,
+ reg_names[REGNO(base_reg_rtx)]);
+
fp_offset -= 2*UNITS_PER_WORD;
}
FILE *file;
int size;
{
- int regno;
- int tsize;
- char *sp_str = reg_names[STACK_POINTER_REGNUM];
- char *fp_str = (!frame_pointer_needed)
- ? sp_str
- : reg_names[FRAME_POINTER_REGNUM];
- tree fndecl = current_function_decl; /* current... is tooo long */
- tree fntype = TREE_TYPE (fndecl);
- tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
- ? DECL_ARGUMENTS (fndecl)
- : 0;
- tree next_arg;
- tree cur_arg;
- char *arg_name = (char *)0;
- CUMULATIVE_ARGS args_so_far;
+ long tsize = current_frame_info.total_size;
ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
- ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+
+ if (debug_info_level != DINFO_LEVEL_TERSE)
+ ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
inside_function = 1;
fputs ("\t.ent\t", file);
assemble_name (file, current_function_name);
fputs ("\n", file);
+
assemble_name (file, current_function_name);
fputs (":\n", file);
"\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
reg_names[ GP_REG_FIRST + 25 ]);
+ tsize = current_frame_info.total_size;
+ if (tsize > 0 && TARGET_ABICALLS)
+ fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET);
+
+ fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n",
+ reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ],
+ tsize,
+ reg_names[31 + GP_REG_FIRST],
+ current_frame_info.var_size,
+ current_frame_info.num_gp,
+ current_frame_info.num_fp,
+ current_function_outgoing_args_size,
+ current_frame_info.extra_size);
+
+ fprintf (file, "\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n",
+ current_frame_info.mask,
+ current_frame_info.gp_save_offset,
+ current_frame_info.fmask,
+ current_frame_info.fp_save_offset);
+}
+
+\f
+/* Expand the prologue into a bunch of separate insns. */
+
+void
+mips_expand_prologue ()
+{
+ int regno;
+ long tsize;
+ rtx tmp_rtx = (rtx)0;
+ char *arg_name = (char *)0;
+ tree fndecl = current_function_decl;
+ tree fntype = TREE_TYPE (fndecl);
+ tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
+ ? DECL_ARGUMENTS (fndecl)
+ : 0;
+ rtx next_arg_reg;
+ int i;
+ tree next_arg;
+ tree cur_arg;
+ CUMULATIVE_ARGS args_so_far;
+
/* Determine the last argument, and get its name. */
+
+ INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+ regno = GP_ARG_FIRST;
+
for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
{
+ tree type = DECL_ARG_TYPE (cur_arg);
+ enum machine_mode passed_mode = TYPE_MODE (type);
+ rtx entry_parm = FUNCTION_ARG (args_so_far,
+ passed_mode,
+ DECL_ARG_TYPE (cur_arg),
+ 1);
+
+ if (entry_parm)
+ {
+ int words;
+
+ /* passed in a register, so will get homed automatically */
+ if (GET_MODE (entry_parm) == BLKmode)
+ words = (int_size_in_bytes (type) + 3) / 4;
+ else
+ words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+
+ regno = REGNO (entry_parm) + words - 1;
+ }
+ else
+ {
+ regno = GP_ARG_LAST+1;
+ break;
+ }
+
+ FUNCTION_ARG_ADVANCE (args_so_far,
+ passed_mode,
+ DECL_ARG_TYPE (cur_arg),
+ 1);
+
next_arg = TREE_CHAIN (cur_arg);
if (next_arg == (tree)0)
{
}
}
- /* If this function is a varargs function, store any registers that
- would normally hold arguments ($4 - $7) on the stack. */
- if ((TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
- || (arg_name
- && (strcmp (arg_name, "__builtin_va_alist") == 0
- || strcmp (arg_name, "va_alist") == 0)))
- {
- tree parm;
+ /* In order to pass small structures by value in registers
+ compatibly with the MIPS compiler, we need to shift the value
+ into the high part of the register. Function_arg has encoded a
+ PARALLEL rtx, holding a vector of adjustments to be made as the
+ next_arg_reg variable, so we split up the insns, and emit them
+ separately. */
- regno = GP_ARG_FIRST;
- INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+ next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
+ if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL)
+ {
+ rtvec adjust = XVEC (next_arg_reg, 0);
+ int num = GET_NUM_ELEM (adjust);
- for (parm = fnargs; (parm && (regno <= GP_ARG_LAST)); parm = TREE_CHAIN (parm))
+ for (i = 0; i < num; i++)
{
- rtx entry_parm;
- enum machine_mode passed_mode;
- tree type;
-
- type = DECL_ARG_TYPE (parm);
- passed_mode = TYPE_MODE (type);
- entry_parm = FUNCTION_ARG (args_so_far, passed_mode,
- DECL_ARG_TYPE (parm), 1);
-
- if (entry_parm)
- {
- int words;
-
- /* passed in a register, so will get homed automatically */
- if (GET_MODE (entry_parm) == BLKmode)
- words = (int_size_in_bytes (type) + 3) / 4;
- else
- words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
-
- regno = REGNO (entry_parm) + words - 1;
- }
- else
- {
- regno = GP_ARG_LAST+1;
- break;
- }
+ rtx pattern = RTVEC_ELT (adjust, i);
+ if (GET_CODE (pattern) != SET
+ || GET_CODE (SET_SRC (pattern)) != ASHIFT)
+ abort_with_insn (pattern, "Insn is not a shift");
- FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
- DECL_ARG_TYPE (parm), 1);
+ PUT_CODE (SET_SRC (pattern), ASHIFTRT);
+ emit_insn (pattern);
}
+ }
- if (regno <= GP_ARG_LAST && (regno & 1) != 0)
+ /* If this function is a varargs function, store any registers that
+ would normally hold arguments ($4 - $7) on the stack. */
+ if ((TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
+ || (arg_name != (char *)0
+ && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
+ || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))
+ {
+ for (; regno <= GP_ARG_LAST; regno++)
{
- fprintf (file, "\tsw\t%s,%d(%s)\t\t# varargs home register\n",
- reg_names[regno], (regno - 4) * 4, sp_str);
- regno++;
- }
+ rtx ptr = stack_pointer_rtx;
+ if (regno != GP_ARG_FIRST)
+ ptr = gen_rtx (PLUS, Pmode, ptr,
+ GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD));
- for (; regno <= GP_ARG_LAST; regno += 2)
- {
- fprintf (file, "\tsd\t%s,%d(%s)\t\t# varargs home register\n",
- reg_names[regno], (regno - 4) * 4, sp_str);
+ emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno));
}
}
- size = MIPS_STACK_ALIGN (size);
- tsize = (!current_frame_info.initialized)
- ? compute_frame_size (size)
- : current_frame_info.total_size;
-
+ tsize = compute_frame_size (get_frame_size ());
if (tsize > 0)
{
- if (tsize <= 32767)
- fprintf (file,
- "\tsubu\t%s,%s,%d\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n",
- sp_str, sp_str, tsize, current_frame_info.var_size,
- current_frame_info.gp_reg_size / 4,
- current_frame_info.fp_reg_size / 8,
- current_function_outgoing_args_size,
- current_frame_info.extra_size);
- else
- fprintf (file,
- "\tli\t%s,%d\n\tsubu\t%s,%s,%s\t\t# vars= %d, regs= %d/%d, args = %d, sfo= %d\n",
- reg_names[MIPS_TEMP1_REGNUM], tsize, sp_str, sp_str,
- reg_names[MIPS_TEMP1_REGNUM], current_frame_info.var_size,
- current_frame_info.gp_reg_size / 4,
- current_frame_info.fp_reg_size / 8,
- current_function_outgoing_args_size,
- current_frame_info.extra_size);
- }
-
- if (TARGET_ABICALLS)
- fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET);
+ rtx tsize_rtx = GEN_INT (tsize);
- fprintf (file, "\t.frame\t%s,%d,%s\n\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n",
- fp_str,
- (frame_pointer_needed) ? 0 : tsize,
- reg_names[31 + GP_REG_FIRST],
- current_frame_info.mask,
- current_frame_info.gp_save_offset,
- current_frame_info.fmask,
- current_frame_info.fp_save_offset);
+ if (tsize > 32767)
+ {
+ tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
+ emit_move_insn (tmp_rtx, tsize_rtx);
+ tsize_rtx = tmp_rtx;
+ }
- save_restore (file, "sw", "sd", "s.d");
+ emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
- if (frame_pointer_needed)
- {
- if (tsize <= 32767)
- fprintf (file, "\taddu\t%s,%s,%d\t# set up frame pointer\n", fp_str, sp_str, tsize);
+ save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
- else
- fprintf (file, "\taddu\t%s,%s,%s\t# set up frame pointer\n", fp_str, sp_str,
- reg_names[MIPS_TEMP1_REGNUM]);
+ if (frame_pointer_needed)
+ emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
}
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. */
+
+ if (profile_flag || profile_block_flag)
+ emit_insn (gen_blockage ());
}
\f
FILE *file;
int size;
{
- int tsize;
+ long tsize;
char *sp_str = reg_names[STACK_POINTER_REGNUM];
char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
rtx epilogue_delay = current_function_epilogue_delay_list;
int noepilogue = FALSE;
int load_nop = FALSE;
int load_only_r31;
+ rtx tmp_rtx = (rtx)0;
+ rtx restore_rtx;
+ int i;
/* The epilogue does not depend on any registers, but the stack
registers, so we assume that if we have 1 pending nop, it can be
fprintf (file, "\t.set\tnoreorder\n");
if (tsize > 32767)
- fprintf (file, "\tli\t%s,%d\n", t1_str, tsize);
-
- if (frame_pointer_needed)
{
- char *fp_str = reg_names[FRAME_POINTER_REGNUM];
- if (tsize > 32767)
- fprintf (file,"\tsubu\t%s,%s,%s\t\t# sp not trusted here\n",
- sp_str, fp_str, t1_str);
- else
- fprintf (file,"\tsubu\t%s,%s,%d\t\t# sp not trusted here\n",
- sp_str, fp_str, tsize);
+ fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize);
+ tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
}
- save_restore (file, "lw", "ld", "l.d");
+ if (frame_pointer_needed)
+ fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n",
+ sp_str, reg_names[FRAME_POINTER_REGNUM]);
+
+ save_restore_insns (FALSE, tmp_rtx, tsize, file);
load_only_r31 = (current_frame_info.mask == (1 << 31)
&& current_frame_info.fmask == 0);
int num_gp_regs = current_frame_info.gp_reg_size / 4;
int num_fp_regs = current_frame_info.fp_reg_size / 8;
int num_regs = num_gp_regs + num_fp_regs;
+ char *name = current_function_name;
+
+ if (name[0] == '*')
+ name++;
dslots_load_total += num_regs;
}
fprintf (stderr,
- "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d\n",
- current_function_name,
+ "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d",
+ name,
(frame_pointer_needed) ? 'y' : 'n',
((current_frame_info.mask & (1 << 31)) != 0) ? 'n' : 'y',
(current_function_calls_alloca) ? 'y' : 'n',
dslots_load_total, dslots_load_filled,
dslots_jump_total, dslots_jump_filled,
num_refs[0], num_refs[1], num_refs[2]);
+
+ if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs)
+ {
+ fprintf (stderr, " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs);
+ prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS;
+ }
+
+ if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs)
+ {
+ fprintf (stderr, " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs);
+ prev_half_pic_refs = HALF_PIC_NUMBER_REFS;
+ }
+
+ fputc ('\n', stderr);
}
/* Reset state info for each function. */
num_refs[2] = 0;
mips_load_reg = (rtx)0;
mips_load_reg2 = (rtx)0;
- number_functions_processed++;
current_frame_info = zero_frame_info;
/* Restore the output file if optimizing the GP (optimizing the GP causes
}
\f
+/* Expand the epilogue into a bunch of separate insns. */
+
+void
+mips_expand_epilogue ()
+{
+ long tsize = current_frame_info.total_size;
+ rtx tsize_rtx = GEN_INT (tsize);
+ rtx tmp_rtx = (rtx)0;
+
+ if (tsize > 32767)
+ {
+ tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
+ emit_move_insn (tmp_rtx, tsize_rtx);
+ tsize_rtx = tmp_rtx;
+ }
+
+ if (tsize > 0)
+ {
+ if (frame_pointer_needed)
+ emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
+
+ save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
+
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
+ }
+
+ emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31)));
+}
+
+\f
/* Define the number of delay slots needed for the function epilogue.
On the mips, we need a slot if either no stack has been allocated,
was created. */
int
-null_epilogue ()
+simple_epilogue_p ()
{
if (!reload_completed)
return 0;