sh.c (trap_exit, sp_switch): New variables.
authorJeff Law <law@gcc.gnu.org>
Thu, 5 Jun 1997 15:52:34 +0000 (09:52 -0600)
committerJeff Law <law@gcc.gnu.org>
Thu, 5 Jun 1997 15:52:34 +0000 (09:52 -0600)
* sh.c (trap_exit, sp_switch): New variables.
(print_operand, case '@'): If trap_exit is nonzero, then use
a trapa instead of an rte/rts to exit the current function.
(sh_expand_prologue): Switch stacks at function entry as needed.
(sh_expand_epilogue): Similarly at function exit.
(function_epilogue): Clear trap_exit and sp_switch too.
(sh_valid_machine_decl_attribute): New function.
* sh.h (VALID_MACHINE_DECL_ATTRIBUTE): Define.
(sp_switch): Declare.
* sh.md (sp_switch_1, sp_switch_2): New named patterns.

From-SVN: r14148

gcc/config/sh/sh.c
gcc/config/sh/sh.h
gcc/config/sh/sh.md

index dd540b773107f90579439597041e6b421fab7d8c..fb723ba5b34b924089c2f2c902dfba026f9e7aa9 100644 (file)
@@ -43,6 +43,16 @@ Boston, MA 02111-1307, USA.  */
    output code for the next function appropriate for an interrupt handler.  */
 int pragma_interrupt;
 
+/* This is set by the trap_exit attribute for functions.   It specifies
+   a trap number to be used in a trapa instruction at function exit
+   (instead of an rte instruction).  */
+int trap_exit;
+
+/* This is used by the sp_switch attribute for functions.  It specifies
+   a variable holding the address of the stack the interrupt function
+   should switch to/from at entry/exit.  */
+rtx sp_switch;
+
 /* This is set by #pragma trapa, and is similar to the above, except that
    the compiler doesn't emit code to preserve all registers.  */
 static int pragma_trapa;
@@ -160,7 +170,7 @@ print_operand_address (stream, x)
    according to modifier code.
 
    '.'  print a .s if insn needs delay slot
-   '@'  print rte or rts depending upon pragma interruptness
+   '@'  print trap, rte or rts depending upon pragma interruptness
    '#'  output a nop if there is nothing to put in the delay slot
    'O'  print a constant without the #
    'R'  print the LSW of a dp value - changes if in little endian
@@ -181,7 +191,9 @@ print_operand (stream, x, code)
        fprintf (stream, ".s");
       break;
     case '@':
-      if (pragma_interrupt)
+      if (trap_exit)
+       fprintf (stream, "trapa #%d", trap_exit);
+      else if (pragma_interrupt)
        fprintf (stream, "rte");
       else
        fprintf (stream, "rts");
@@ -2670,6 +2682,10 @@ sh_expand_prologue ()
         }
     }
 
+  /* If we're supposed to switch stacks at function entry, do so now.  */
+  if (sp_switch)
+    emit_insn (gen_sp_switch_1 ());
+
   push_regs (live_regs_mask, live_regs_mask2);
 
   output_stack_adjust (-get_frame_size (), stack_pointer_rtx, 3);
@@ -2712,6 +2728,10 @@ sh_expand_epilogue ()
 
   output_stack_adjust (extra_push + current_function_pretend_args_size,
                       stack_pointer_rtx, 7);
+
+  /* Switch back to the normal stack if necessary.  */
+  if (sp_switch)
+    emit_insn (gen_sp_switch_2 ());
 }
 
 /* Clear variables at function end.  */
@@ -2721,7 +2741,8 @@ function_epilogue (stream, size)
      FILE *stream;
      int size;
 {
-  pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0;
+  trap_exit = pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0;
+  sp_switch = NULL_RTX;
 }
 
 rtx
@@ -2847,6 +2868,76 @@ handle_pragma (file, t)
 
   return retval;
 }
+/* Return nonzero if ATTR is a valid attribute for DECL.
+   ATTRIBUTES are any existing attributes and ARGS are the arguments
+   supplied with ATTR.
+
+   Supported attributes:
+
+   interrupt_handler -- specifies this function is an interrupt handler.
+
+   sp_switch -- specifies an alternate stack for an interrupt handler
+   to run on.
+
+   trap_exit -- use a trapa to exit an interrupt function intead of
+   an rte instruction.  */
+
+int
+sh_valid_machine_decl_attribute (decl, attributes, attr, args)
+     tree decl;
+     tree attributes;
+     tree attr;
+     tree args;
+{
+  int retval = 0;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    return 0;
+
+  if (is_attribute_p ("interrupt_handler", attr))
+    {
+      pragma_interrupt = 1;
+      return 1;
+    }
+
+  if (is_attribute_p ("sp_switch", attr))
+    {
+      /* The sp_switch attribute only has meaning for interrupt functions.  */
+      if (!pragma_interrupt)
+       return 0;
+
+      /* sp_switch must have an argument.  */
+      if (!args || TREE_CODE (args) != TREE_LIST)
+       return 0;
+
+      /* The argument must be a constant string.  */
+      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+       return 0;
+
+      sp_switch = gen_rtx (SYMBOL_REF, VOIDmode,
+                          TREE_STRING_POINTER (TREE_VALUE (args)));
+      return 1;
+    }
+
+  if (is_attribute_p ("trap_exit", attr))
+    {
+      /* The trap_exit attribute only has meaning for interrupt functions.  */
+      if (!pragma_interrupt)
+       return 0;
+
+      /* trap_exit must have an argument.  */
+      if (!args || TREE_CODE (args) != TREE_LIST)
+       return 0;
+
+      /* The argument must be a constant integer.  */
+      if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+       return 0;
+
+      trap_exit = TREE_INT_CST_LOW (TREE_VALUE (args));
+      return 1;
+    }
+}
+
 \f
 /* Predicates used by the templates.  */
 
index 022c9bcb4c42d2aabe7baf6434c14d07a3433f8f..ce5e26a3300b47dace4de2ed83ceeffb556938cf 100644 (file)
@@ -1639,6 +1639,18 @@ extern char *output_far_jump();
 
 extern int pragma_interrupt;
 
+/* Set to an RTX containing the address of the stack to switch to
+   for interrupt functions.  */
+extern struct rtx_def *sp_switch;
+
+/* A C expression whose value is nonzero if IDENTIFIER with arguments ARGS
+   is a valid machine specific attribute for DECL.
+   The attributes in ATTRIBUTES have previously been assigned to DECL.  */
+extern int sh_valid_machine_decl_attribute ();
+#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRIBUTES, IDENTIFIER, ARGS) \
+sh_valid_machine_decl_attribute (DECL, ATTRIBUTES, IDENTIFIER, ARGS)
+
+
 #define MOVE_RATIO (TARGET_SMALLCODE ? 2 : 16)
 \f
 /* Instructions with unfilled delay slots take up an extra two bytes for
index e5ee8685df038511414dcb0d3c1988db7356e699..d0f79ff37184b580bbb6a3be3c86f0c4c8d78fbf 100644 (file)
           && REGNO (SUBREG_REG (operands[2])) >= FIRST_FP_REG))
    && reg_unused_after (operands[0], insn)"
   "fmov.s      @(%0,%1),%2")
+
+;; Switch to a new stack with its address in sp_switch (a SYMBOL_REF).  */
+(define_insn "sp_switch_1"
+  [(const_int 1)]
+  ""
+  "*
+{
+  rtx xoperands[1];
+
+  xoperands[0] = sp_switch;
+  output_asm_insn (\"mov.l r0,@-r15\;mov.l %0,r0\", xoperands);
+  output_asm_insn (\"mov.l @r0,r0\;mov.l r15,@-r0\", xoperands);
+  return \"mov r0,r15\";
+}"
+  [(set_attr "length" "10")])
+   (set_attr "type" "move")])
+
+;; Switch back to the original stack for interrupt funtions with the
+;; sp_switch attribute.  */
+(define_insn "sp_switch_2"
+  [(const_int 2)]
+  ""
+  "mov.l @r15+,r15\;mov.l @r15+,r0"
+  [(set_attr "length" "4")])