diff options
-rw-r--r-- | gcc/ChangeLog | 31 | ||||
-rw-r--r-- | gcc/config/cris/cris-protos.h | 2 | ||||
-rw-r--r-- | gcc/config/cris/cris.c | 873 | ||||
-rw-r--r-- | gcc/config/cris/cris.h | 13 | ||||
-rw-r--r-- | gcc/config/cris/cris.md | 65 |
5 files changed, 656 insertions, 328 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3ef8573..ba90977 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,36 @@ 2005-04-13 Hans-Peter Nilsson <hp@axis.com> + CRIS prologue as RTL. + * config/cris/cris-protos.h (cris_emit_movem_store) + (cris_expand_prologue): Prototype. + * config/cris/cris.c (struct machine_function): New member + stdarg_regs. + (cfa_label_num, cris_target_asm_function_prologue): Remove. + (TARGET_ASM_FUNCTION_PROLOGUE): Don't override. + (cris_general_operand_or_gotless_symbol): Accept CRIS_UNSPEC_GOT. + (cris_load_multiple_op, cris_return_address_on_stack) + (cris_return_address_on_stack_for_return): ISO-Cify. + (cris_store_multiple_op): New predicate function. + (cris_expand_prologue, cris_emit_movem_store): New functions. + (cris_print_operand) <case 'O'>: Handle modifications other than + post-increment. + (cris_symbol, cris_got_symbol): Return 0 for CRIS_UNSPEC_GOT. + (cris_gotless_symbol): Return 1 for CRIS_UNSPEC_GOT. + (cris_gen_movem_load): Rearrange slightly to make local variable + src a parameter, removing osrc. + (cris_setup_incoming_varargs): Set machine_function member + stdarg_regs to correspond to the number of registers that need to + be saved. + * config/cris/cris.h (EXTRA_CONSTRAINT_S): Accept + CRIS_UNSPEC_GOT. + (PREDICATE_CODES): Add cris_store_multiple_op. Make + cris_general_operand_or_gotless_symbol accept UNSPEC. + * config/cris/cris.md (CRIS_UNSPEC_GOT): New constant. + ("*movsi_internal") <alternative 8>: Handle CRIS_UNSPEC_GOT. + ("*cris_store_multiple"): New pattern. Tweak common comment above + this and "*cris_load_multiple". + ("prologue"): New define_expand. + * config/cris/cris.md ("epilogue"): Conditionalize on TARGET_PROLOGUE_EPILOGUE. diff --git a/gcc/config/cris/cris-protos.h b/gcc/config/cris/cris-protos.h index ecd9632..571f179 100644 --- a/gcc/config/cris/cris-protos.h +++ b/gcc/config/cris/cris-protos.h @@ -45,9 +45,11 @@ extern void cris_asm_output_symbol_ref (FILE *, rtx); extern bool cris_output_addr_const_extra (FILE *, rtx); extern int cris_cfun_uses_pic_table (void); extern rtx cris_gen_movem_load (rtx, rtx, int); +extern rtx cris_emit_movem_store (rtx, rtx, int, bool); #endif /* RTX_CODE */ extern void cris_asm_output_label_ref (FILE *, char *); extern void cris_target_asm_named_section (const char *, unsigned int, tree); +extern void cris_expand_prologue (void); extern void cris_expand_epilogue (void); extern void cris_expand_return (bool); extern bool cris_return_address_on_stack_for_return (void); diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c index a9512a6..f7822d4 100644 --- a/gcc/config/cris/cris.c +++ b/gcc/config/cris/cris.c @@ -74,6 +74,11 @@ enum cris_retinsn_type struct machine_function GTY(()) { int needs_return_address_on_stack; + + /* This is the number of registers we save in the prologue due to + stdarg. */ + int stdarg_regs; + enum cris_retinsn_type return_type; }; @@ -111,8 +116,6 @@ static int cris_initial_frame_pointer_offset (void); static int saved_regs_mentioned (rtx); -static void cris_target_asm_function_prologue (FILE *, HOST_WIDE_INT); - static void cris_operand_lossage (const char *, rtx); static int cris_reg_saved_in_regsave_area (unsigned int, bool); @@ -171,9 +174,6 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION; #undef TARGET_ASM_UNALIGNED_DI_OP #define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue - #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK cris_asm_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK @@ -431,12 +431,13 @@ cris_general_operand_or_symbol (rtx op, enum machine_mode mode) /* Since a PIC symbol without a GOT entry is not a general_operand, we have to have a predicate that matches it. We use this in the expanded - "movsi" anonymous pattern for PIC symbols. */ + "movsi" anonymous pattern. */ int cris_general_operand_or_gotless_symbol (rtx op, enum machine_mode mode) { return general_operand (op, mode) + || (GET_CODE (op) == UNSPEC && XINT (op, 1) == CRIS_UNSPEC_GOT) || (CONSTANT_P (op) && cris_gotless_symbol (op)); } @@ -556,13 +557,124 @@ cris_movem_load_rest_p (rtx op, int offs) /* Predicate for the parallel contents in a movem from-memory. */ int -cris_load_multiple_op (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +cris_load_multiple_op (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return cris_movem_load_rest_p (op, 0); } +/* Predicate for the parallel contents in a movem to-memory. */ + +int +cris_store_multiple_op (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + int reg_count = XVECLEN (op, 0); + rtx dest; + rtx dest_addr; + rtx dest_base; + int i; + rtx elt; + int setno; + int regno_dir = 1; + int regno = 0; + int offset = 0; + + /* Perform a quick check so we don't blow up below. FIXME: Adjust for + other than (MEM reg) and (MEM (PLUS reg const)). */ + if (reg_count <= 1) + return 0; + + elt = XVECEXP (op, 0, 0); + + if (GET_CODE (elt) != SET) + return 0; + + dest = SET_DEST (elt); + + if (GET_CODE (SET_SRC (elt)) != REG + || GET_CODE (dest) != MEM) + return 0; + + dest_addr = XEXP (dest, 0); + + /* Check a possible post-inc indicator. */ + if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS) + { + rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 0); + rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1); + + reg_count--; + + if (reg_count == 1 + || !REG_P (reg) + || !REG_P (SET_DEST (XVECEXP (op, 0, 1))) + || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, 1))) + || GET_CODE (inc) != CONST_INT + /* Support increment by number of registers, and by the offset + of the destination, if it has the form (MEM (PLUS reg + offset)). */ + || !((REG_P (dest_addr) + && REGNO (dest_addr) == REGNO (reg) + && INTVAL (inc) == (HOST_WIDE_INT) reg_count * 4) + || (GET_CODE (dest_addr) == PLUS + && REG_P (XEXP (dest_addr, 0)) + && REGNO (XEXP (dest_addr, 0)) == REGNO (reg) + && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT + && INTVAL (XEXP (dest_addr, 1)) == INTVAL (inc)))) + return 0; + + i = 2; + } + else + i = 1; + + /* FIXME: These two only for pre-v32. */ + regno_dir = -1; + regno = reg_count - 1; + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned int) regno + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode) + return 0; + + if (REG_P (dest_addr)) + { + dest_base = dest_addr; + offset = 0; + } + else if (GET_CODE (dest_addr) == PLUS + && REG_P (XEXP (dest_addr, 0)) + && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT) + { + dest_base = XEXP (dest_addr, 0); + offset = INTVAL (XEXP (dest_addr, 1)); + } + else + return 0; + + for (setno = 1; i < XVECLEN (op, 0); setno++, i++) + { + rtx elt = XVECEXP (op, 0, i); + regno += regno_dir; + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned int) regno + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_base) + || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != setno * 4 + offset) + return 0; + } + + return 1; +} + /* The CONDITIONAL_REGISTER_USAGE worker. */ void @@ -775,280 +887,6 @@ cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used) || regno == EH_RETURN_DATA_REGNO (3))); } -/* This variable belongs to cris_target_asm_function_prologue but must - be located outside it for GTY reasons. */ -static GTY(()) unsigned long cfa_label_num = 0; - -/* Textual function prologue. */ - -static void -cris_target_asm_function_prologue (FILE *file, HOST_WIDE_INT size) -{ - int regno; - - /* Shorten the used name for readability. */ - int cfoa_size = current_function_outgoing_args_size; - int last_movem_reg = -1; - int doing_dwarf = dwarf2out_do_frame (); - int framesize; - int faked_args_size = 0; - int cfa_write_offset = 0; - static char cfa_label[30]; - bool return_address_on_stack = cris_return_address_on_stack (); - bool got_really_used = current_function_uses_pic_offset_table; - - /* Don't do anything if no prologues or epilogues are wanted. */ - if (!TARGET_PROLOGUE_EPILOGUE) - return; - - if (size < 0) - abort (); - - /* Align the size to what's best for the CPU model. */ - if (TARGET_STACK_ALIGN) - size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; - - if (current_function_pretend_args_size) - { - int pretend = current_function_pretend_args_size; - for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1; - pretend > 0; - regno--, pretend -= 4) - { - fprintf (file, "\tpush $%s\n", reg_names[regno]); - faked_args_size += 4; - } - } - - framesize = faked_args_size; - - if (doing_dwarf) - { - /* FIXME: Slightly redundant calculation, as we do the same in - pieces below. This offset must be the total adjustment of the - stack-pointer. We can then def_cfa call at the end of this - function with the current implementation of execute_cfa_insn, but - that wouldn't really be clean. */ - - int cfa_offset - = faked_args_size - + (return_address_on_stack ? 4 : 0) - + (frame_pointer_needed ? 4 : 0); - - int cfa_reg; - - if (frame_pointer_needed) - cfa_reg = FRAME_POINTER_REGNUM; - else - { - cfa_reg = STACK_POINTER_REGNUM; - cfa_offset += cris_initial_frame_pointer_offset (); - } - - ASM_GENERATE_INTERNAL_LABEL (cfa_label, "LCFIT", - cfa_label_num++); - dwarf2out_def_cfa (cfa_label, cfa_reg, cfa_offset); - - cfa_write_offset = - faked_args_size - 4; - } - - /* Save SRP if not a leaf function. */ - if (return_address_on_stack) - { - fprintf (file, "\tPush $srp\n"); - framesize += 4; - - if (doing_dwarf) - { - dwarf2out_return_save (cfa_label, cfa_write_offset); - cfa_write_offset -= 4; - } - } - - /* Set up frame pointer if needed. */ - if (frame_pointer_needed) - { - fprintf (file, "\tpush $%s\n\tmove.d $sp,$%s\n", - reg_names[FRAME_POINTER_REGNUM], - reg_names[FRAME_POINTER_REGNUM]); - framesize += 4; - - if (doing_dwarf) - { - dwarf2out_reg_save (cfa_label, FRAME_POINTER_REGNUM, - cfa_write_offset); - cfa_write_offset -= 4; - } - } - - /* Local vars are located above saved regs. */ - cfa_write_offset -= size; - - /* Get a contiguous sequence of registers, starting with r0, that need - to be saved. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - if (cris_reg_saved_in_regsave_area (regno, got_really_used)) - { - /* Check if movem may be used for registers so far. */ - if (regno == last_movem_reg + 1) - /* Yes, update next expected register. */ - last_movem_reg++; - else - { - /* We cannot use movem for all registers. We have to flush - any movem:ed registers we got so far. */ - if (last_movem_reg != -1) - { - /* It is a win to use a side-effect assignment for - 64 <= size <= 128. But side-effect on movem was - not usable for CRIS v0..3. Also only do it if - side-effects insns are allowed. */ - if ((last_movem_reg + 1) * 4 + size >= 64 - && (last_movem_reg + 1) * 4 + size <= 128 - && cris_cpu_version >= CRIS_CPU_SVINTO - && TARGET_SIDE_EFFECT_PREFIXES) - fprintf (file, "\tmovem $%s,[$sp=$sp-"HOST_WIDE_INT_PRINT_DEC"]\n", - reg_names[last_movem_reg], - (last_movem_reg + 1) * 4 + size); - else - { - /* Avoid printing multiple subsequent sub:s for sp. */ - fprintf (file, "\tsub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n", - ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) - * 4 + size), - (last_movem_reg + 1) * 4 + size); - - fprintf (file, "\tmovem $%s,[$sp]\n", - reg_names[last_movem_reg]); - } - - framesize += (last_movem_reg + 1) * 4 + size; - - if (TARGET_PDEBUG) - fprintf (file, "; frame "HOST_WIDE_INT_PRINT_DEC - ", #regs %d, bytes %d args %d\n", - size, - last_movem_reg + 1, - (last_movem_reg + 1) * 4, - current_function_args_size); - - last_movem_reg = -1; - size = 0; - } - else if (size > 0) - { - /* Local vars on stack, but there are no movem:s. - Just allocate space. */ - fprintf (file, "\tSub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n", - ADDITIVE_SIZE_MODIFIER (size), - size); - framesize += size; - size = 0; - } - - fprintf (file, "\tPush $%s\n", reg_names[regno]); - framesize += 4; - } - - if (doing_dwarf) - { - /* Registers are stored lowest numbered at highest address, - which matches the loop order; we just need to update the - write-offset. */ - dwarf2out_reg_save (cfa_label, regno, cfa_write_offset); - cfa_write_offset -= 4; - } - } - } - - /* Check after, if we can movem all registers. This is the normal - case. */ - if (last_movem_reg != -1) - { - /* Side-effect assignment on movem was not supported for CRIS v0..3, - and don't do it if we're asked not to. - - The movem is already accounted for, for unwind. */ - - if ((last_movem_reg + 1) * 4 + size >= 64 - && (last_movem_reg + 1) * 4 + size <= 128 - && cris_cpu_version >= CRIS_CPU_SVINTO - && TARGET_SIDE_EFFECT_PREFIXES) - fprintf (file, "\tmovem $%s,[$sp=$sp-"HOST_WIDE_INT_PRINT_DEC"]\n", - reg_names[last_movem_reg], - (last_movem_reg+1) * 4 + size); - else - { - /* Avoid printing multiple subsequent sub:s for sp. FIXME: - Clean up the conditional expression. */ - fprintf (file, "\tsub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n", - ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) * 4 + size), - (last_movem_reg + 1) * 4 + size); - /* To be compatible with v0..v3 means we do not use an assignment - addressing mode with movem. We normally don't need that - anyway. It would only be slightly more efficient for 64..128 - bytes frame size. */ - fprintf (file, "\tmovem $%s,[$sp]\n", reg_names[last_movem_reg]); - } - - framesize += (last_movem_reg + 1) * 4 + size; - - if (TARGET_PDEBUG) - fprintf (file, "; frame "HOST_WIDE_INT_PRINT_DEC - ", #regs %d, bytes %d args %d\n", - size, - last_movem_reg + 1, - (last_movem_reg + 1) * 4, - current_function_args_size); - - /* We have to put outgoing argument space after regs. */ - if (cfoa_size) - { - /* This does not need to be accounted for, for unwind. */ - - fprintf (file, "\tSub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (cfoa_size), - cfoa_size); - framesize += cfoa_size; - } - } - else if ((size + cfoa_size) > 0) - { - /* This does not need to be accounted for, for unwind. */ - - /* Local vars on stack, and we could not use movem. Add a sub here. */ - fprintf (file, "\tSub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n", - ADDITIVE_SIZE_MODIFIER (size + cfoa_size), - cfoa_size + size); - framesize += size + cfoa_size; - } - - /* Set up the PIC register. */ - if (current_function_uses_pic_offset_table) - fprintf (file, "\tmove.d $pc,$%s\n\tsub.d .:GOTOFF,$%s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM]); - - if (doing_dwarf) - ASM_OUTPUT_LABEL (file, cfa_label); - - if (TARGET_PDEBUG) - fprintf (file, - "; parm #%d @ %d; frame " HOST_WIDE_INT_PRINT_DEC - ", FP-SP is %d; leaf: %s%s; fp %s, outg: %d arg %d\n", - CRIS_MAX_ARGS_IN_REGS + 1, FIRST_PARM_OFFSET (0), - get_frame_size (), - cris_initial_frame_pointer_offset (), - leaf_function_p () ? "yes" : "no", - return_address_on_stack ? "no" :"yes", - frame_pointer_needed ? "yes" : "no", - cfoa_size, current_function_args_size); - - if (cris_max_stackframe && framesize > cris_max_stackframe) - warning ("stackframe too big: %d bytes", framesize); -} - /* Return nonzero if there are regs mentioned in the insn that are not all in the call_used regs. This is part of the decision whether an insn can be put in the epilogue. */ @@ -1176,11 +1014,24 @@ cris_print_operand (FILE *file, rtx x, int code) ? XEXP (SET_SRC (XVECEXP (x, 0, 0)), 0) : XEXP (SET_DEST (XVECEXP (x, 0, 0)), 0); - /* The second item can be a (set reg (plus reg const)) to denote a - post-increment. */ + /* The second item can be a (set reg (plus reg const)) to denote + a modification. */ if (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS) - addr = gen_rtx_POST_INC (SImode, addr); - + { + /* It's a post-increment, if the address is a naked (reg). */ + if (REG_P (addr)) + addr = gen_rtx_POST_INC (SImode, addr); + else + { + /* Otherwise, it's a side-effect; RN=RN+M. */ + fprintf (file, "[$%s=$%s%s%d]", + reg_names [REGNO (SET_DEST (XVECEXP (x, 0, 1)))], + reg_names [REGNO (XEXP (addr, 0))], + INTVAL (XEXP (addr, 1)) < 0 ? "" : "+", + (int) INTVAL (XEXP (addr, 1))); + return; + } + } output_address (addr); } return; @@ -1561,7 +1412,7 @@ cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED) there. */ bool -cris_return_address_on_stack () +cris_return_address_on_stack (void) { return regs_ever_live[CRIS_SRP_REGNUM] || cfun->machine->needs_return_address_on_stack; @@ -1571,7 +1422,7 @@ cris_return_address_on_stack () there. */ bool -cris_return_address_on_stack_for_return () +cris_return_address_on_stack_for_return (void) { return cfun->machine->return_type == CRIS_RETINSN_RET ? false : cris_return_address_on_stack (); @@ -2325,6 +2176,8 @@ cris_symbol (rtx x) return 1; case UNSPEC: + if (XINT (x, 1) == CRIS_UNSPEC_GOT) + return 0; /* A PLT reference. */ ASSERT_PLT_UNSPEC (x); return 1; @@ -2363,6 +2216,8 @@ cris_gotless_symbol (rtx x) switch (GET_CODE (x)) { case UNSPEC: + if (XINT (x, 1) == CRIS_UNSPEC_GOT) + return 1; ASSERT_PLT_UNSPEC (x); return 1; @@ -2422,6 +2277,8 @@ cris_got_symbol (rtx x) switch (GET_CODE (x)) { case UNSPEC: + if (XINT (x, 1) == CRIS_UNSPEC_GOT) + return 0; ASSERT_PLT_UNSPEC (x); return 0; @@ -2806,6 +2663,277 @@ cris_split_movdx (rtx *operands) return val; } +/* The expander for the prologue pattern name. */ + +void +cris_expand_prologue (void) +{ + int regno; + int size = get_frame_size (); + /* Shorten the used name for readability. */ + int cfoa_size = current_function_outgoing_args_size; + int last_movem_reg = -1; + int framesize = 0; + rtx mem, insn; + int return_address_on_stack = cris_return_address_on_stack (); + int got_really_used = current_function_uses_pic_offset_table; + int n_movem_regs = 0; + int pretend = current_function_pretend_args_size; + + /* Don't do anything if no prologues or epilogues are wanted. */ + if (!TARGET_PROLOGUE_EPILOGUE) + return; + + if (size < 0) + abort (); + + /* Align the size to what's best for the CPU model. */ + if (TARGET_STACK_ALIGN) + size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; + + if (pretend) + { + /* See also cris_setup_incoming_varargs where + cfun->machine->stdarg_regs is set. There are other setters of + current_function_pretend_args_size than stdarg handling, like + for an argument passed with parts in R13 and stack. We must + not store R13 into the pretend-area for that case, as GCC does + that itself. "Our" store would be marked as redundant and GCC + will attempt to remove it, which will then be flagged as an + internal error; trying to remove a frame-related insn. */ + int stdarg_regs = cfun->machine->stdarg_regs; + + framesize += pretend; + + for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1; + stdarg_regs > 0; + regno--, pretend -= 4, stdarg_regs--) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4))); + /* FIXME: When dwarf2 frame output and unless asynchronous + exceptions, make dwarf2 bundle together all stack + adjustments like it does for registers between stack + adjustments. */ + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_varargs_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno)); + + /* Note the absence of RTX_FRAME_RELATED_P on the above insn: + the value isn't restored, so we don't want to tell dwarf2 + that it's been stored to stack, else EH handling info would + get confused. */ + } + + /* For other setters of current_function_pretend_args_size, we + just adjust the stack by leaving the remaining size in + "pretend", handled below. */ + } + + /* Save SRP if not a leaf function. */ + if (return_address_on_stack) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - pretend))); + pretend = 0; + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM)); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += 4; + } + + /* Set up the frame pointer, if needed. */ + if (frame_pointer_needed) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - pretend))); + pretend = 0; + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, frame_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + + insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + + framesize += 4; + } + + /* Between frame-pointer and saved registers lie the area for local + variables. If we get here with "pretended" size remaining, count + it into the general stack size. */ + size += pretend; + + /* Get a contiguous sequence of registers, starting with R0, that need + to be saved. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) + { + n_movem_regs++; + + /* Check if movem may be used for registers so far. */ + if (regno == last_movem_reg + 1) + /* Yes, update next expected register. */ + last_movem_reg = regno; + else + { + /* We cannot use movem for all registers. We have to flush + any movem:ed registers we got so far. */ + if (last_movem_reg != -1) + { + int n_saved + = (n_movem_regs == 1) ? 1 : last_movem_reg + 1; + + /* It is a win to use a side-effect assignment for + 64 <= size <= 128. But side-effect on movem was + not usable for CRIS v0..3. Also only do it if + side-effects insns are allowed. */ + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1) + && TARGET_SIDE_EFFECT_PREFIXES) + { + mem + = gen_rtx_MEM (SImode, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn + = cris_emit_movem_store (mem, GEN_INT (n_saved), + -(n_saved * 4 + size), + true); + } + else + { + insn + = gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), + 0, true); + } + + framesize += n_saved * 4 + size; + last_movem_reg = -1; + size = 0; + } + + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - size))); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno)); + RTX_FRAME_RELATED_P (insn) = 1; + + framesize += 4 + size; + size = 0; + } + } + } + + /* Check after, if we could movem all registers. This is the normal case. */ + if (last_movem_reg != -1) + { + int n_saved + = (n_movem_regs == 1) ? 1 : last_movem_reg + 1; + + /* Side-effect on movem was not usable for CRIS v0..3. Also only + do it if side-effects insns are allowed. */ + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1) + && TARGET_SIDE_EFFECT_PREFIXES) + { + mem + = gen_rtx_MEM (SImode, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), + -(n_saved * 4 + size), true); + } + else + { + insn + = gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), 0, true); + } + + framesize += n_saved * 4 + size; + /* We have to put outgoing argument space after regs. */ + if (cfoa_size) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -cfoa_size))); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += cfoa_size; + } + } + else if ((size + cfoa_size) > 0) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(cfoa_size + size)))); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += size + cfoa_size; + } + + /* Set up the PIC register, if it is used. */ + if (got_really_used) + { + rtx got + = gen_rtx_UNSPEC (SImode, gen_rtvec (1, const0_rtx), CRIS_UNSPEC_GOT); + emit_move_insn (pic_offset_table_rtx, got); + + /* FIXME: This is a cover-up for flow2 messing up; it doesn't + follow exceptional paths and tries to delete the GOT load as + unused, if it isn't used on the non-exceptional paths. Other + ports have similar or other cover-ups, or plain bugs marking + the GOT register load as maybe-dead. To see this, remove the + line below and try libsupc++/vec.cc or a trivial + "static void y (); void x () {try {y ();} catch (...) {}}". */ + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); + } + + if (cris_max_stackframe && framesize > cris_max_stackframe) + warning ("stackframe too big: %d bytes", framesize); +} + /* The expander for the epilogue pattern. */ void @@ -2991,14 +3119,13 @@ cris_expand_epilogue (void) /* Worker function for generating movem from mem for load_multiple. */ rtx -cris_gen_movem_load (rtx osrc, rtx nregs_rtx, int nprefix) +cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix) { int nregs = INTVAL (nregs_rtx); rtvec vec; int eltno = 1; int i; - rtx srcreg = XEXP (osrc, 0); - rtx src = osrc; + rtx srcreg = XEXP (src, 0); unsigned int regno = nregs - 1; int regno_inc = -1; @@ -3009,25 +3136,26 @@ cris_gen_movem_load (rtx osrc, rtx nregs_rtx, int nprefix) abort (); /* Don't use movem for just one insn. The insns are equivalent except - for the pipeline hazard; movem does not forward the loaded - registers so there's a three cycles penalty for use. */ + for the pipeline hazard (on v32); movem does not forward the loaded + registers so there's a three cycles penalty for their use. */ if (nregs == 1) - return gen_movsi (gen_rtx_REG (SImode, regno), osrc); + return gen_movsi (gen_rtx_REG (SImode, 0), src); vec = rtvec_alloc (nprefix + nregs - + (GET_CODE (XEXP (osrc, 0)) == POST_INC)); - src = replace_equiv_address (osrc, srcreg); - RTVEC_ELT (vec, nprefix) - = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src); - regno += regno_inc; + + (GET_CODE (XEXP (src, 0)) == POST_INC)); - if (GET_CODE (XEXP (osrc, 0)) == POST_INC) + if (GET_CODE (XEXP (src, 0)) == POST_INC) { RTVEC_ELT (vec, nprefix + 1) = gen_rtx_SET (VOIDmode, srcreg, plus_constant (srcreg, nregs * 4)); eltno++; } + src = replace_equiv_address (src, srcreg); + RTVEC_ELT (vec, nprefix) + = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src); + regno += regno_inc; + for (i = 1; i < nregs; i++, eltno++) { RTVEC_ELT (vec, nprefix + eltno) @@ -3039,6 +3167,136 @@ cris_gen_movem_load (rtx osrc, rtx nregs_rtx, int nprefix) return gen_rtx_PARALLEL (VOIDmode, vec); } +/* Worker function for generating movem to mem. If FRAME_RELATED, notes + are added that the dwarf2 machinery understands. */ + +rtx +cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment, + bool frame_related) +{ + int nregs = INTVAL (nregs_rtx); + rtvec vec; + int eltno = 1; + int i; + rtx insn; + rtx destreg = XEXP (dest, 0); + unsigned int regno = nregs - 1; + int regno_inc = -1; + + if (GET_CODE (destreg) == POST_INC) + increment += nregs * 4; + + if (GET_CODE (destreg) == POST_INC || GET_CODE (destreg) == PLUS) + destreg = XEXP (destreg, 0); + + if (!REG_P (destreg)) + abort (); + + /* Don't use movem for just one insn. The insns are equivalent except + for the pipeline hazard (on v32); movem does not forward the loaded + registers so there's a three cycles penalty for use. */ + if (nregs == 1) + { + rtx mov = gen_rtx_SET (VOIDmode, dest, gen_rtx_REG (SImode, 0)); + + if (increment == 0) + { + insn = emit_insn (mov); + if (frame_related) + RTX_FRAME_RELATED_P (insn) = 1; + return insn; + } + + /* If there was a request for a side-effect, create the ordinary + parallel. */ + vec = rtvec_alloc (2); + + RTVEC_ELT (vec, 0) = mov; + RTVEC_ELT (vec, 1) = gen_rtx_SET (VOIDmode, destreg, + plus_constant (destreg, increment)); + if (frame_related) + { + RTX_FRAME_RELATED_P (mov) = 1; + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1; + } + } + else + { + vec = rtvec_alloc (nregs + (increment != 0 ? 1 : 0)); + RTVEC_ELT (vec, 0) + = gen_rtx_SET (VOIDmode, + replace_equiv_address (dest, + plus_constant (destreg, + increment)), + gen_rtx_REG (SImode, regno)); + regno += regno_inc; + + /* The dwarf2 info wants this mark on each component in a parallel + that's part of the prologue (though it's optional on the first + component). */ + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 0)) = 1; + + if (increment != 0) + { + RTVEC_ELT (vec, 1) + = gen_rtx_SET (VOIDmode, destreg, + plus_constant (destreg, + increment != 0 + ? increment : nregs * 4)); + eltno++; + + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1; + + /* Don't call adjust_address_nv on a post-incremented address if + we can help it. */ + if (GET_CODE (XEXP (dest, 0)) == POST_INC) + dest = replace_equiv_address (dest, destreg); + } + + for (i = 1; i < nregs; i++, eltno++) + { + RTVEC_ELT (vec, eltno) + = gen_rtx_SET (VOIDmode, adjust_address_nv (dest, SImode, i * 4), + gen_rtx_REG (SImode, regno)); + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, eltno)) = 1; + regno += regno_inc; + } + } + + insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, vec)); + + /* Because dwarf2out.c handles the insns in a parallel as a sequence, + we need to keep the stack adjustment separate, after the + MEM-setters. Else the stack-adjustment in the second component of + the parallel would be mishandled; the offsets for the SETs that + follow it would be wrong. We prepare for this by adding a + REG_FRAME_RELATED_EXPR with the MEM-setting parts in a SEQUENCE + followed by the increment. Note that we have FRAME_RELATED_P on + all the SETs, including the original stack adjustment SET in the + parallel. */ + if (frame_related) + { + if (increment != 0) + { + rtx seq = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (nregs + 1)); + XVECEXP (seq, 0, 0) = XVECEXP (PATTERN (insn), 0, 0); + for (i = 1; i < nregs; i++) + XVECEXP (seq, 0, i) = XVECEXP (PATTERN (insn), 0, i + 1); + XVECEXP (seq, 0, nregs) = XVECEXP (PATTERN (insn), 0, 1); + REG_NOTES (insn) + = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, seq, + REG_NOTES (insn)); + } + + RTX_FRAME_RELATED_P (insn) = 1; + } + + return insn; +} + /* Use from within code, from e.g. PRINT_OPERAND and PRINT_OPERAND_ADDRESS. Macros used in output_addr_const need to emit different things depending on whether code operand or constant is @@ -3175,13 +3433,16 @@ cris_setup_incoming_varargs (CUMULATIVE_ARGS *ca, int second_time) { if (ca->regs < CRIS_MAX_ARGS_IN_REGS) - *pretend_arg_size = (CRIS_MAX_ARGS_IN_REGS - ca->regs) * 4; - if (TARGET_PDEBUG) { - fprintf (asm_out_file, - "\n; VA:: ANSI: %d args before, anon @ #%d, %dtime\n", - ca->regs, *pretend_arg_size, second_time); + int stdarg_regs = CRIS_MAX_ARGS_IN_REGS - ca->regs; + cfun->machine->stdarg_regs = stdarg_regs; + *pretend_arg_size = stdarg_regs * 4; } + + if (TARGET_PDEBUG) + fprintf (asm_out_file, + "\n; VA:: ANSI: %d args before, anon @ #%d, %dtime\n", + ca->regs, *pretend_arg_size, second_time); } /* Return true if TYPE must be passed by invisible reference. diff --git a/gcc/config/cris/cris.h b/gcc/config/cris/cris.h index 96c7303..1b3a8f2 100644 --- a/gcc/config/cris/cris.h +++ b/gcc/config/cris/cris.h @@ -854,8 +854,13 @@ enum reg_class && BIAP_INDEX_P (XEXP (XEXP (X, 0), 0)))))) \ ) -#define EXTRA_CONSTRAINT_S(X) \ - (flag_pic && CONSTANT_P (X) && cris_gotless_symbol (X)) +/* We're kind of out of constraints, so we use "S" for both gotless + symbols and the GOT-address load. Both must go in a general register + only: for pre-V32, arithmetic is done on the destination. */ +#define EXTRA_CONSTRAINT_S(X) \ + (flag_pic \ + && ((CONSTANT_P (X) && cris_gotless_symbol (X)) \ + || (GET_CODE (X) == UNSPEC && XINT ((X), 1) == CRIS_UNSPEC_GOT))) #define EXTRA_CONSTRAINT_U(X) \ (flag_pic && CONSTANT_P (X) && cris_got_symbol (X)) @@ -1629,6 +1634,8 @@ struct cum_args {int regs;}; {MEM}}, \ {"cris_load_multiple_op", \ {PARALLEL}}, \ + {"cris_store_multiple_op", \ + {PARALLEL}}, \ {"cris_bdap_operand", \ {SUBREG, REG, LABEL_REF, SYMBOL_REF, MEM, CONST_INT, \ CONST_DOUBLE, CONST, SIGN_EXTEND}}, \ @@ -1637,7 +1644,7 @@ struct cum_args {int regs;}; CONST_DOUBLE, CONST, SIGN_EXTEND, MULT}}, \ {"cris_general_operand_or_gotless_symbol", \ {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \ - LABEL_REF, SUBREG, REG, MEM}}, \ + LABEL_REF, SUBREG, REG, MEM, UNSPEC}}, \ {"cris_general_operand_or_symbol", \ {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \ LABEL_REF, SUBREG, REG, MEM}}, \ diff --git a/gcc/config/cris/cris.md b/gcc/config/cris/cris.md index 64e689b..9456857 100644 --- a/gcc/config/cris/cris.md +++ b/gcc/config/cris/cris.md @@ -59,11 +59,12 @@ ;; 0 PLT reference from call expansion: operand 0 is the address, ;; the mode is VOIDmode. Always wrapped in CONST. ;; 1 Stack frame deallocation barrier. +;; 2 The address of the global offset table as a source operand. (define_constants [(CRIS_UNSPEC_PLT 0) - (CRIS_UNSPEC_FRAME_DEALLOC 1)]) - + (CRIS_UNSPEC_FRAME_DEALLOC 1) + (CRIS_UNSPEC_GOT 2)]) ;; Register numbers. (define_constants @@ -1067,21 +1068,33 @@ } return \"move.d %1,%0\"; - case 8: - /* FIXME: Try and split this into pieces GCC makes better code of, - than this multi-insn pattern. Synopsis: wrap the GOT-relative - symbol into an unspec, and when PIC, recognize the unspec - everywhere a symbol is normally recognized. (The PIC register - should be recognized by GCC as pic_offset_table_rtx when needed - and similar for PC.) Each component can then be optimized with - the rest of the code; it should be possible to have a constant - term added on an unspec. Don't forget to add a REG_EQUAL (or - is it REG_EQUIV) note to the destination. It might not be - worth it. Measure. - - Note that the 'v' modifier makes PLT references be output as - sym:PLT rather than [rPIC+sym:GOTPLT]. */ - return \"move.d %v1,%0\;add.d %P1,%0\"; + case 8: + /* FIXME: Try and split this into pieces GCC makes better code of, + than this multi-insn pattern. Synopsis: wrap the GOT-relative + symbol into an unspec, and when PIC, recognize the unspec + everywhere a symbol is normally recognized. (The PIC register + should be recognized by GCC as pic_offset_table_rtx when needed + and similar for PC.) Each component can then be optimized with + the rest of the code; it should be possible to have a constant + term added on an unspec. Don't forget to add a REG_EQUAL (or + is it REG_EQUIV) note to the destination. It might not be + worth it. Measure. + + Note that the 'v' modifier makes PLT references be output as + sym:PLT rather than [rPIC+sym:GOTPLT]. */ + if (GET_CODE (operands[1]) == UNSPEC + && XINT (operands[1], 1) == CRIS_UNSPEC_GOT) + { + /* We clobber cc0 rather than set it to GOT. Should not + matter, though. */ + CC_STATUS_INIT; + if (REGNO (operands[0]) != PIC_OFFSET_TABLE_REGNUM) + abort (); + + return \"move.d $pc,%0\;sub.d .:GOTOFF,%0\"; + } + + return \"move.d %v1,%0\;add.d %P1,%0\"; default: return \"BOGUS: %1 to %0\"; @@ -1387,8 +1400,8 @@ move %1,%0" [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no,yes,yes,yes,no,yes,no")]) -;; Note that the order of the registers is the reverse of that of the -;; standard pattern "load_multiple". +;; Note that the memory layout of the registers is the reverse of that +;; of the standard patterns "load_multiple" and "store_multiple". (define_insn "*cris_load_multiple" [(match_parallel 0 "cris_load_multiple_op" [(set (match_operand:SI 1 "register_operand" "=r,r") @@ -1404,6 +1417,15 @@ ;; FIXME: temporary change until all insn lengths are correctly ;; described. FIXME: have better target control over bb-reorder. (set_attr "length" "0")]) + +(define_insn "*cris_store_multiple" + [(match_parallel 0 "cris_store_multiple_op" + [(set (match_operand:SI 2 "memory_operand" "=Q,m") + (match_operand:SI 1 "register_operand" "r,r"))])] + "" + "movem %o0,%O0" + [(set_attr "cc" "none") + (set_attr "slottable" "yes,no")]) ;; Sign- and zero-extend insns with standard names. @@ -3515,6 +3537,11 @@ (const_string "no") (const_string "has_slot")))]) +(define_expand "prologue" + [(const_int 0)] + "TARGET_PROLOGUE_EPILOGUE" + "cris_expand_prologue (); DONE;") + ;; Note that the (return) from the expander itself is always the last ;; insn in the epilogue. (define_expand "epilogue" |