pdp11.md (define_c_enum "unspecv"): New.
authorRichard Henderson <rth@redhat.com>
Sat, 9 Jul 2011 15:41:31 +0000 (08:41 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Sat, 9 Jul 2011 15:41:31 +0000 (08:41 -0700)
* 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

gcc/ChangeLog
gcc/config/pdp11/pdp11-protos.h
gcc/config/pdp11/pdp11.c
gcc/config/pdp11/pdp11.md

index 9291ee814d67996321857012e4a2c240d8e4a734..cbc027fb93305a1d60c13b3b8c3f0916055b882c 100644 (file)
@@ -1,3 +1,18 @@
+2011-07-09  Richard Henderson  <rth@redhat.com>
+
+       * 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.
+
 2011-07-09  Richard Henderson  <rth@redhat.com>
 
        * config/rs6000/rs6000.c (rs6000_output_function_prologue): Don't
index 56ad909e10ea151b057a25e56d881f54c9da04ae..bb13309f34bb38dd646d58a0c98397023fba033c 100644 (file)
@@ -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);
index 870b94715185f1c7ea5a56ad81e7ca76a44c54f9..63e99865dd663a84cfdba060d2f65637d18b2b7b 100644 (file)
@@ -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
 \f
-/*
-   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;
index 1c6542685b742afb53546ec94e84e9a09515512e..23a8665c9744d531f4a3044e2a48b6bd4d40a1a4 100644 (file)
 (include "predicates.md")
 (include "constraints.md")
 
+(define_c_enum "unspecv"
+  [
+    UNSPECV_BLOCKAGE
+    UNSPECV_SETD
+    UNSPECV_SETI
+  ])
+
 (define_constants
   [
    ;; Register numbers
 
 ;; 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)