diff options
author | Richard Henderson <rth@redhat.com> | 2011-07-09 08:41:31 -0700 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2011-07-09 08:41:31 -0700 |
commit | 8662b2ba92816ad1e1a461ef9fe3b2c3e9d21905 (patch) | |
tree | 3315e7a8955f0c9e0124919e79de774a9cf458c7 /gcc/config | |
parent | 28b21ebbdb7bc48a1f3de36bf4f98a58a00c54ec (diff) | |
download | gcc-8662b2ba92816ad1e1a461ef9fe3b2c3e9d21905.zip gcc-8662b2ba92816ad1e1a461ef9fe3b2c3e9d21905.tar.gz gcc-8662b2ba92816ad1e1a461ef9fe3b2c3e9d21905.tar.bz2 |
pdp11.md (define_c_enum "unspecv"): New.
* config/pdp11/pdp11.md (define_c_enum "unspecv"): New.
(prologue, epilogue): New.
(return, *rts): New.
(blockage, setd, seti): New.
* config/pdp11/pdp11.c (TARGET_ASM_FUNCTION_PROLOGUE): Remove.
(TARGET_ASM_FUNCTION_EPILOGUE): Remove.
(pdp11_saved_regno): New.
(pdp11_expand_prologue): Rename from pdp11_output_function_prologue;
generate rtl instead of text.
(pdp11_expand_epilogue): Similarly from pdp11_output_function_epilogue.
(pdp11_sp_frame_offset): Export. Use pdp11_saved_regno.
* config/pdp11/pdp11-protos.h: Update.
From-SVN: r176082
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/pdp11/pdp11-protos.h | 3 | ||||
-rw-r--r-- | gcc/config/pdp11/pdp11.c | 364 | ||||
-rw-r--r-- | gcc/config/pdp11/pdp11.md | 51 |
3 files changed, 240 insertions, 178 deletions
diff --git a/gcc/config/pdp11/pdp11-protos.h b/gcc/config/pdp11/pdp11-protos.h index 56ad909..bb13309 100644 --- a/gcc/config/pdp11/pdp11-protos.h +++ b/gcc/config/pdp11/pdp11-protos.h @@ -38,6 +38,7 @@ typedef enum { no_action, dec_before, inc_after } pdp11_action; typedef enum { little, either, big } pdp11_partorder; extern bool pdp11_expand_operands (rtx *, rtx [][2], int, pdp11_action *, pdp11_partorder); +extern int pdp11_sp_frame_offset (void); extern int pdp11_initial_elimination_offset (int, int); extern enum reg_class pdp11_regno_reg_class (int); @@ -45,3 +46,5 @@ extern enum reg_class pdp11_regno_reg_class (int); extern void output_ascii (FILE *, const char *, int); extern void pdp11_asm_output_var (FILE *, const char *, int, int, bool); +extern void pdp11_expand_prologue (void); +extern void pdp11_expand_epilogue (void); diff --git a/gcc/config/pdp11/pdp11.c b/gcc/config/pdp11/pdp11.c index 870b947..63e9986 100644 --- a/gcc/config/pdp11/pdp11.c +++ b/gcc/config/pdp11/pdp11.c @@ -141,8 +141,6 @@ decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, static const char *singlemove_string (rtx *); static bool pdp11_assemble_integer (rtx, unsigned int, int); -static void pdp11_output_function_prologue (FILE *, HOST_WIDE_INT); -static void pdp11_output_function_epilogue (FILE *, HOST_WIDE_INT); static bool pdp11_rtx_costs (rtx, int, int, int *, bool); static bool pdp11_return_in_memory (const_tree, const_tree); static rtx pdp11_function_value (const_tree, const_tree, bool); @@ -166,11 +164,6 @@ static bool pdp11_legitimate_constant_p (enum machine_mode, rtx); #undef TARGET_ASM_INTEGER #define TARGET_ASM_INTEGER pdp11_assemble_integer -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE pdp11_output_function_prologue -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE pdp11_output_function_epilogue - #undef TARGET_ASM_OPEN_PAREN #define TARGET_ASM_OPEN_PAREN "[" #undef TARGET_ASM_CLOSE_PAREN @@ -227,95 +220,92 @@ static bool pdp11_legitimate_constant_p (enum machine_mode, rtx); #undef TARGET_LEGITIMATE_CONSTANT_P #define TARGET_LEGITIMATE_CONSTANT_P pdp11_legitimate_constant_p -/* - stream is a stdio stream to output the code to. - size is an int: how many units of temporary storage to allocate. - Refer to the array `regs_ever_live' to determine which registers - to save; `regs_ever_live[I]' is nonzero if register number I - is ever used in the function. This macro is responsible for - knowing which registers should not be saved even if used. -*/ +/* A helper function to determine if REGNO should be saved in the + current function's stack frame. */ -static void -pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size) -{ - HOST_WIDE_INT fsize = ((size) + 1) & ~1; - int regno; - int via_ac = -1; +static inline bool +pdp11_saved_regno (unsigned regno) +{ + return !call_used_regs[regno] && df_regs_ever_live_p (regno); +} - fprintf (stream, - "\n\t; /* function prologue %s*/\n", - current_function_name ()); +/* Expand the function prologue. */ - /* if we are outputting code for main, - the switch FPU to right mode if TARGET_FPU */ - if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU) +void +pdp11_expand_prologue (void) +{ + HOST_WIDE_INT fsize = get_frame_size (); + unsigned regno; + rtx x, via_ac = NULL; + + /* If we are outputting code for main, the switch FPU to the + right mode if TARGET_FPU. */ + if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU) { - fprintf(stream, - "\t;/* switch cpu to double float, single integer */\n"); - fprintf(stream, "\tsetd\n"); - fprintf(stream, "\tseti\n\n"); + emit_insn (gen_setd ()); + emit_insn (gen_seti ()); } - if (frame_pointer_needed) - { - fprintf(stream, "\tmov r5, -(sp)\n"); - fprintf(stream, "\tmov sp, r5\n"); - } - else + if (frame_pointer_needed) { - /* DON'T SAVE FP */ + x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (Pmode, x); + emit_move_insn (x, hard_frame_pointer_rtx); + + emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); } - /* make frame */ - if (fsize) - asm_fprintf (stream, "\tsub $%#wo, sp\n", fsize); - - /* save CPU registers */ - for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++) - if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) - if (! ((regno == FRAME_POINTER_REGNUM) - && frame_pointer_needed)) - fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]); - /* fpu regs saving */ - - /* via_ac specifies the ac to use for saving ac4, ac5 */ - via_ac = -1; - - for (regno = AC0_REGNUM; regno <= AC5_REGNUM ; regno++) + /* Make frame. */ + if (fsize) { - /* ac0 - ac3 */ - if (LOAD_FPU_REG_P(regno) - && df_regs_ever_live_p (regno) - && ! call_used_regs[regno]) - { - fprintf (stream, "\tstd %s, -(sp)\n", reg_names[regno]); - via_ac = regno; - } - - /* maybe make ac4, ac5 call used regs?? */ - /* ac4 - ac5 */ - if (NO_LOAD_FPU_REG_P(regno) - && df_regs_ever_live_p (regno) - && ! call_used_regs[regno]) - { - gcc_assert (via_ac != -1); - fprintf (stream, "\tldd %s, %s\n", - reg_names[regno], reg_names[via_ac]); - fprintf (stream, "\tstd %s, -(sp)\n", reg_names[via_ac]); - } + emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-fsize))); + + /* Prevent frame references via the frame pointer from being + scheduled before the frame is allocated. */ + if (frame_pointer_needed) + emit_insn (gen_blockage ()); } - fprintf (stream, "\t;/* end of prologue */\n\n"); + /* Save CPU registers. */ + for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++) + if (pdp11_saved_regno (regno) + && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed)) + { + x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (Pmode, x); + emit_move_insn (x, gen_rtx_REG (Pmode, regno)); + } + + /* Save FPU registers. */ + for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++) + if (pdp11_saved_regno (regno)) + { + x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (DFmode, x); + via_ac = gen_rtx_REG (DFmode, regno); + emit_move_insn (x, via_ac); + } + + /* ??? Maybe make ac4, ac5 call used regs?? */ + for (regno = AC4_REGNUM; regno <= AC5_REGNUM; regno++) + if (pdp11_saved_regno (regno)) + { + gcc_assert (via_ac != NULL); + emit_move_insn (via_ac, gen_rtx_REG (DFmode, regno)); + + x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (DFmode, x); + emit_move_insn (x, via_ac); + } } -/* - The function epilogue should not depend on the current stack pointer! +/* The function epilogue should not depend on the current stack pointer! It should use the frame pointer only. This is mandatory because of alloca; we also take advantage of it to omit stack adjustments before returning. */ -/* maybe we can make leaf functions faster by switching to the +/* Maybe we can make leaf functions faster by switching to the second register file - this way we don't have to save regs! leaf functions are ~ 50% of all functions (dynamically!) @@ -328,109 +318,127 @@ pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size) maybe as option if you want to generate code for kernel mode? */ -static void -pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) +void +pdp11_expand_epilogue (void) { - HOST_WIDE_INT fsize = ((size) + 1) & ~1; - int i, j, k; + HOST_WIDE_INT fsize = get_frame_size (); + unsigned regno; + rtx x, reg, via_ac = NULL; - int via_ac; - - fprintf (stream, "\n\t; /*function epilogue */\n"); + if (pdp11_saved_regno (AC4_REGNUM) || pdp11_saved_regno (AC5_REGNUM)) + { + /* Find a temporary with which to restore AC4/5. */ + for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++) + if (pdp11_saved_regno (regno)) + { + via_ac = gen_rtx_REG (DFmode, regno); + break; + } + } - if (frame_pointer_needed) - { - /* hope this is safe - m68k does it also .... */ - df_set_regs_ever_live (FRAME_POINTER_REGNUM, false); - - for (i = PC_REGNUM, j = 0 ; i >= 0 ; i--) - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) - j++; - - /* remember # of pushed bytes for CPU regs */ - k = 2*j; - - /* change fp -> r5 due to the compile error on libgcc2.c */ - for (i = PC_REGNUM ; i >= R0_REGNUM ; i--) - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) - fprintf(stream, "\tmov %#" HOST_WIDE_INT_PRINT "o(r5), %s\n", - (-fsize-2*j--)&0xffff, reg_names[i]); - - /* get ACs */ - via_ac = AC5_REGNUM; - - for (i = AC5_REGNUM; i >= AC0_REGNUM; i--) - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) - { - via_ac = i; - k += 8; - } - - for (i = AC5_REGNUM; i >= AC0_REGNUM; i--) - { - if (LOAD_FPU_REG_P(i) - && df_regs_ever_live_p (i) - && ! call_used_regs[i]) - { - fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n", - (-fsize-k)&0xffff, reg_names[i]); - k -= 8; - } - - if (NO_LOAD_FPU_REG_P(i) - && df_regs_ever_live_p (i) - && ! call_used_regs[i]) - { - gcc_assert (LOAD_FPU_REG_P(via_ac)); - - fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n", - (-fsize-k)&0xffff, reg_names[via_ac]); - fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]); - k -= 8; - } - } - - fprintf(stream, "\tmov r5, sp\n"); - fprintf (stream, "\tmov (sp)+, r5\n"); - } - else - { - via_ac = AC5_REGNUM; - - /* get ACs */ - for (i = AC5_REGNUM; i >= AC0_REGNUM; i--) - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) - via_ac = i; - - for (i = AC5_REGNUM; i >= AC0_REGNUM; i--) + /* If possible, restore registers via pops. */ + if (!frame_pointer_needed || current_function_sp_is_unchanging) + { + /* Restore registers via pops. */ + + for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--) + if (pdp11_saved_regno (regno)) + { + x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (DFmode, x); + reg = gen_rtx_REG (DFmode, regno); + + if (LOAD_FPU_REG_P (regno)) + emit_move_insn (reg, x); + else + { + emit_move_insn (via_ac, x); + emit_move_insn (reg, via_ac); + } + } + + for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--) + if (pdp11_saved_regno (regno) + && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed)) + { + x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (Pmode, x); + emit_move_insn (gen_rtx_REG (Pmode, regno), x); + } + } + else + { + /* Restore registers via moves. */ + /* ??? If more than a few registers need to be restored, it's smaller + to generate a pointer through which we can emit pops. Consider + that moves cost 2*NREG words and pops cost NREG+3 words. This + means that the crossover is NREG=3. + + Possible registers to use are: + (1) The first call-saved general register. This register will + be restored with the last pop. + (2) R1, if it's not used as a return register. + (3) FP itself. This option may result in +4 words, since we + may need two add imm,rn instructions instead of just one. + This also has the downside that we're not representing + the unwind info in any way, so during the epilogue the + debugger may get lost. */ + + HOST_WIDE_INT ofs = -pdp11_sp_frame_offset (); + + for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--) + if (pdp11_saved_regno (regno)) + { + x = plus_constant (hard_frame_pointer_rtx, ofs); + x = gen_frame_mem (DFmode, x); + reg = gen_rtx_REG (DFmode, regno); + + if (LOAD_FPU_REG_P (regno)) + emit_move_insn (reg, x); + else + { + emit_move_insn (via_ac, x); + emit_move_insn (reg, via_ac); + } + ofs += 8; + } + + for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--) + if (pdp11_saved_regno (regno) + && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed)) + { + x = plus_constant (hard_frame_pointer_rtx, ofs); + x = gen_frame_mem (Pmode, x); + emit_move_insn (gen_rtx_REG (Pmode, regno), x); + ofs += 2; + } + } + + /* Deallocate the stack frame. */ + if (fsize) + { + /* Prevent frame references via any pointer from being + scheduled after the frame is deallocated. */ + emit_insn (gen_blockage ()); + + if (frame_pointer_needed) { - if (LOAD_FPU_REG_P(i) - && df_regs_ever_live_p (i) - && ! call_used_regs[i]) - fprintf(stream, "\tldd (sp)+, %s\n", reg_names[i]); - - if (NO_LOAD_FPU_REG_P(i) - && df_regs_ever_live_p (i) - && ! call_used_regs[i]) - { - gcc_assert (LOAD_FPU_REG_P(via_ac)); - - fprintf(stream, "\tldd (sp)+, %s\n", reg_names[via_ac]); - fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]); - } + /* We can deallocate the frame with a single move. */ + emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx); } + else + emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (fsize))); + } - for (i = PC_REGNUM; i >= 0; i--) - if (df_regs_ever_live_p (i) && !call_used_regs[i]) - fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]); - - if (fsize) - fprintf((stream), "\tadd $%#" HOST_WIDE_INT_PRINT "o, sp\n", - (fsize)&0xffff); - } - - fprintf (stream, "\trts pc\n"); - fprintf (stream, "\t;/* end of epilogue*/\n\n\n"); + if (frame_pointer_needed) + { + x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx); + x = gen_frame_mem (Pmode, x); + emit_move_insn (hard_frame_pointer_rtx, x); + } + + emit_jump_insn (gen_return ()); } /* Return the best assembler insn template @@ -1570,16 +1578,16 @@ pdp11_regno_reg_class (int regno) } -static int +int pdp11_sp_frame_offset (void) { int offset = 0, regno; offset = get_frame_size(); for (regno = 0; regno <= PC_REGNUM; regno++) - if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) + if (pdp11_saved_regno (regno)) offset += 2; for (regno = AC0_REGNUM; regno <= AC5_REGNUM; regno++) - if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) + if (pdp11_saved_regno (regno)) offset += 8; return offset; diff --git a/gcc/config/pdp11/pdp11.md b/gcc/config/pdp11/pdp11.md index 1c65426..23a8665 100644 --- a/gcc/config/pdp11/pdp11.md +++ b/gcc/config/pdp11/pdp11.md @@ -22,6 +22,13 @@ (include "predicates.md") (include "constraints.md") +(define_c_enum "unspecv" + [ + UNSPECV_BLOCKAGE + UNSPECV_SETD + UNSPECV_SETI + ]) + (define_constants [ ;; Register numbers @@ -104,6 +111,50 @@ ;; define function units +;; Prologue and epilogue support. + +(define_expand "prologue" + [(const_int 0)] + "" +{ + pdp11_expand_prologue (); + DONE; +}) + +(define_expand "epilogue" + [(const_int 0)] + "" +{ + pdp11_expand_epilogue (); + DONE; +}) + +(define_expand "return" + [(return)] + "reload_completed && !frame_pointer_needed && pdp11_sp_frame_offset () == 0" + "") + +(define_insn "*rts" + [(return)] + "" + "rts pc") + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] + "" + "" + [(set_attr "length" "0")]) + +(define_insn "setd" + [(unspec_volatile [(const_int 0)] UNSPECV_SETD)] + "" + "setd") + +(define_insn "seti" + [(unspec_volatile [(const_int 0)] UNSPECV_SETI)] + "" + "seti") + ;; arithmetic - values here immediately when next insn issued ;; or does it mean the number of cycles after this insn was issued? ;; how do I say that fpu insns use cpu also? (pre-interaction phase) |