From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24577 invoked by alias); 8 Jul 2011 22:13:51 -0000 Received: (qmail 24567 invoked by uid 22791); 8 Jul 2011 22:13:49 -0000 X-SWARE-Spam-Status: No, hits=-6.2 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 08 Jul 2011 22:13:27 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p68MDP8u004956 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 8 Jul 2011 18:13:25 -0400 Received: from anchor.twiddle.net (vpn-224-88.phx2.redhat.com [10.3.224.88]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p68MDOYa008690; Fri, 8 Jul 2011 18:13:25 -0400 Message-ID: <4E178104.9020406@redhat.com> Date: Fri, 08 Jul 2011 22:59:00 -0000 From: Richard Henderson User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110428 Fedora/3.1.10-1.fc15 Thunderbird/3.1.10 MIME-Version: 1.0 To: Paul Koning CC: GCC Patches Subject: [pdp11] Emit prologue as rtl References: <4D8A0703.9090306@codesourcery.com> <4D8A095C.8050809@codesourcery.com> <4E15D2DC.9010704@codesourcery.com> <4E15E61C.7010007@redhat.com> In-Reply-To: Content-Type: multipart/mixed; boundary="------------090807080905040904020401" X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2011-07/txt/msg00687.txt.bz2 This is a multi-part message in MIME format. --------------090807080905040904020401 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 577 This appears to do the right thing. I didn't bother with markers for unwind info, since pdp11 is limited to a.out and thus will never use dwarf2. There are improvements that could be made. I added some comments to that effect but did not otherwise change the code generation. Note that the existence of the epilogue and return patterns will by themselves cause changes. In particular, basic block re-ordering will likely move the epilogue into the middle of the function, placing unlikely code paths later; something that was not possible with a text-based epilogue. r~ --------------090807080905040904020401 Content-Type: text/plain; name="d-pdp11-1" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="d-pdp11-1" Content-length: 16679 * 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. 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) --------------090807080905040904020401--