public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] MIPS function attributes for interrupt handlers
@ 2008-10-14 20:29 Fu, Chao-Ying
  2008-10-15 23:47 ` Richard Sandiford
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Fu, Chao-Ying @ 2008-10-14 20:29 UTC (permalink / raw)
  To: gcc-patches; +Cc: Lau, David

Hi All,

  Four new function attributes are added for MIPS interrupt handlers.
They are "naked", "interrupt", "vector", and "at_vector".  This patch
is ported from Microchip GCC source code for PIC32 that can be downloaded
from the following link (in the middle of the web page).
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2615&dDocName=en532454
NOTE: The equivalent pragma support is not ported.

  For the usage of these attributes, please check the attached patch
for "extend.texi".  Reading Chapter 3 "Interrupts" of "MPLAB C32 C Compiler User's Guide"  
http://ww1.microchip.com/downloads/en/DeviceDoc/MPLAB%20C32%20User%20Guide%2051686a.pdf
helps a lot.

  Some parts of interrupt handler supports are specific to PIC32, ex:
the shadow register set is tied to the interrupt priority level 7,
and the device is a MIPS32r2 CPU.
But, these can be changed to be more general in the future, if other
MIPS devices come out.

  Is this patch ok?  Thanks a lot!

Regards,
Chao-ying

gcc/ChangeLog
2008-10-14  Chao-ying Fu  <fu@mips.com>
		James Grosbach <james.grosbach@microchip.com>

	* config/mips/mips.c (mips_frame_info): Add int_save_offset, int_sp_offset,
	has_interrupt_context_p, has_hilo_context_p for interupt info.
	(mips_attribute_table): Add interrupt, vector, at_vector, naked.
	(function_type_tag): New enum.
	(current_function_type): New global variable.
	(vector_dispatch_spec): New structure.
	(mips_naked_decl_p, mips_interrupt_type_p, mips_interrupt_attribute,
	mips_at_vector_attribute, mips_add_vector_dispatch_entry,
	mips_vector_attribute): New functions.
	(mips_function_ok_for_sibcall): Return false for interrupt handlers.
	(mips_file_end): New function.
	(mips_register_interrupt_context_p): New function.
	(mips_save_reg_p): Check registers for interrupt handlers.
	(mips_compute_frame_info): Add supports for interrupt context.
	(mips_output_function_prologue, mips_output_function_epilogue):
	Output code for interrupt handlers.
	(mips_expand_prologue): Just return for naked functions.
	Support interrupt handlers.
	(mips_expand_epilogue): Support naked functions.
	Support interrupt handlers.
	(mips_can_use_return_insn): Return false for interrupt handlers
	and naked functions.
	(mips_epilogue_uses): New function.
	(TARGET_ASM_FILE_END): Define.

	* config/mips/mips.md (return_from_epilogue): New instruction
	that will be called for interrupt handlers.
	
	* config/mips/mips-protos.h (mips_epilogue_uses): Declare.

	* config/mips/mips.h (CAUSE_IPL, SR_IPL, SR_IE, SR_EXL, SR_ERL):
	New defines.
	(EPILOGUE_USES): Change to a function call.
	
	* doc/extend.texi (Function Attributes): Document interrupt,
	at_vector, vector, naked for MIPS.

gcc/testsuite/ChangeLog
2008-10-14  Chao-ying Fu  <fu@mips.com>

        * gcc.target/mips/interrupt_handler.c: New tests.
	
Index: gcc4x/gcc/gcc/config/mips/mips.c
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.c	2008-10-13 17:29:33.000000000 -0700
+++ gcc4x/gcc/gcc/config/mips/mips.c	2008-10-13 18:23:06.000000000 -0700
@@ -265,20 +265,32 @@ struct mips_frame_info GTY(()) {
   unsigned int num_gp;
   unsigned int num_fp;
 
-  /* The offset of the topmost GPR and FPR save slots from the top of
-     the frame, or zero if no such slots are needed.  */
+  /* The number of interrupt reg saved.  */
+  unsigned int num_interrupt_reg;
+
+  /* The offset of the topmost GPR and FPR and interrupt regs save slots from
+     the top of the frame, or zero if no such slots are needed.  */
   HOST_WIDE_INT gp_save_offset;
   HOST_WIDE_INT fp_save_offset;
+  HOST_WIDE_INT int_save_offset;
 
   /* Likewise, but giving offsets from the bottom of the frame.  */
   HOST_WIDE_INT gp_sp_offset;
   HOST_WIDE_INT fp_sp_offset;
+  HOST_WIDE_INT int_sp_offset;
 
   /* The offset of arg_pointer_rtx from frame_pointer_rtx.  */
   HOST_WIDE_INT arg_pointer_offset;
 
   /* The offset of hard_frame_pointer_rtx from frame_pointer_rtx.  */
   HOST_WIDE_INT hard_frame_pointer_offset;
+
+  /* True if interrupt context is saved.  */
+  bool has_interrupt_context_p;
+
+  /* True if hi and lo registers are saved.  Will only be true for
+     interrupts.  Need to take care of 4 hi-lo pairs.  */
+  bool has_hilo_context_p[4];
 };
 
 struct machine_function GTY(()) {
@@ -542,6 +554,10 @@ const enum reg_class mips_regno_to_class
   ALL_REGS,	ALL_REGS,	ALL_REGS,	ALL_REGS
 };
 
+static tree mips_interrupt_attribute (tree *, tree, tree, int, bool *);
+static tree mips_vector_attribute (tree *, tree, tree, int, bool *);
+static tree mips_at_vector_attribute (tree *, tree, tree, int, bool *);
+
 /* The value of TARGET_ATTRIBUTE_TABLE.  */
 const struct attribute_spec mips_attribute_table[] = {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
@@ -554,8 +570,34 @@ const struct attribute_spec mips_attribu
      code generation but don't carry other semantics.  */
   { "mips16", 	   0, 0, true,  false, false, NULL },
   { "nomips16",    0, 0, true,  false, false, NULL },
+  /* Allow functions to be specified as interrupt handlers */
+  { "interrupt",   1, 1, false, true,  true, mips_interrupt_attribute },
+  { "vector",      1, 64,true,  false, false, mips_vector_attribute },
+  { "at_vector",   1, 1, true,  false, false, mips_at_vector_attribute },
+  { "naked",       0, 0, true,  false, false, NULL },
   { NULL,	   0, 0, false, false, false, NULL }
 };
+
+/* Is the current function an interrupt? If so, how is context handled?  */
+enum function_type_tag {
+  DONT_KNOW,
+  NON_INTERRUPT,
+  SOFTWARE_CONTEXT_SAVE,
+  SRS_CONTEXT_SAVE
+};
+enum function_type_tag current_function_type = DONT_KNOW;
+
+/* MIPS devices also allow interrupt vector dispactch functions
+   to be defined via an attribute (of the interrupt handler).
+   We keep a list of the dispatch functions needed and emit them at
+   the end of processing the translation unit.  */
+struct vector_dispatch_spec
+{
+  const char *target;	/* Target function name.  */
+  int vector_number;	/* Exception vector table number.  */
+  struct vector_dispatch_spec *next;
+};
+struct vector_dispatch_spec *vector_dispatch_list_head;
 \f
 /* A table describing all the processors GCC knows about.  Names are
    matched in the order listed.  The first mention of an ISA level is
@@ -1172,6 +1214,162 @@ mips_nomips16_decl_p (const_tree decl)
   return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
 }
 
+/* Predicate to test for "naked" function attribute.  */
+
+static bool
+mips_naked_decl_p (const_tree decl)
+{
+  return lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
+/* Check if the interrupt attribute is set for a function. If it is, return
+   the IPL identifier, else NULL.  */
+
+static tree
+mips_interrupt_type_p (tree type)
+{
+  tree attr = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type));
+  if (attr)
+    attr = TREE_VALUE (attr);
+  return attr;
+}
+
+/* Function to handle this attribute.  NODE points to the node to which
+   the attribute is to be applied.  If a DECL, it should be modified in
+   place; if a TYPE, a copy should be created.  NAME is the name of the
+   attribute (possibly with leading or trailing __).  ARGS is the TREE_LIST
+   of the arguments (which may be NULL).  FLAGS gives further information
+   about the context of the attribute.  Afterwards, the attributes will
+   be added to the DECL_ATTRIBUTES or TYPE_ATTRIBUTES, as appropriate,
+   unless *NO_ADD_ATTRS is set to true (which should be done on error,
+   as well as in any other cases when the attributes should not be added
+   to the DECL or TYPE).  Depending on FLAGS, any attributes to be
+   applied to another type or DECL later may be returned;
+   otherwise the return value should be NULL_TREE.  This pointer may be
+   NULL if no special handling is required beyond the checks implied
+   by the rest of this structure.  */
+
+static tree
+mips_interrupt_attribute (tree *node ATTRIBUTE_UNUSED,
+			  tree name ATTRIBUTE_UNUSED, tree args,
+			  int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  /* We want to validate that the argument isn't bogus. There should
+     be one and only one argument in the args TREE_LIST and it should
+     be an identifier of the form "single" or "ipl[0-7]".  */
+
+  /* We can assert one argument since that should be enforced by
+     the parser from the attribute table.  */
+  gcc_assert (TREE_CHAIN (args) == NULL);
+
+  if (TREE_CODE (TREE_VALUE (args)) != IDENTIFIER_NODE
+      || (strcmp ("single", IDENTIFIER_POINTER (TREE_VALUE (args))) != 0
+          && ((strncmp ("ipl", IDENTIFIER_POINTER (TREE_VALUE (args)), 3) != 0
+               && strncmp ("IPL", IDENTIFIER_POINTER (TREE_VALUE(args)),3)!= 0)
+	      || IDENTIFIER_POINTER (TREE_VALUE (args))[3] > '7'
+	      || IDENTIFIER_POINTER (TREE_VALUE (args))[3] < '0')))
+    {
+      error ("Interrupt priority must be specified as 'single' or 'ipl0' - 'ipl7'");
+      *no_add_attrs = 1;
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
+
+/* Function to handle at_vector attribute.  */
+
+static tree
+mips_at_vector_attribute (tree *node ATTRIBUTE_UNUSED,
+			  tree name ATTRIBUTE_UNUSED, tree args,
+			  int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  tree decl = *node;
+  char scn_name[11];
+
+  /* If this attribute isn't on the actual function declaration, we
+     ignore it.  */
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    return NULL_TREE;
+
+  if (DECL_SECTION_NAME (decl) != NULL_TREE)
+    {
+      error ("the 'at_vector' attribute cannot be used with the 'section' attribute");
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* The argument must be an integer constant between 0 and 63.  */
+  if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST ||
+      (int)TREE_INT_CST_LOW (TREE_VALUE (args)) < 0 ||
+      (int)TREE_INT_CST_LOW (TREE_VALUE (args)) > 63)
+    {
+      *no_add_attrs = 1;
+      error ("IRQ number must be an integer between 0 and 63");
+      return NULL_TREE;
+    }
+
+  /* Now mark the decl as going into the section for the indicated vector.  */
+  sprintf (scn_name, ".vector_%d", (int)TREE_INT_CST_LOW (TREE_VALUE (args)));
+  DECL_SECTION_NAME (decl) = build_string (strlen (scn_name), scn_name);
+
+  return NULL_TREE;
+}
+
+/* Utility function to add an entry to the vector dispatch list.  */
+
+static void
+mips_add_vector_dispatch_entry (const char *target_name, int vector_number)
+{
+  struct vector_dispatch_spec *dispatch_entry;
+
+  /* Add the vector to the list of dispatch functions to emit.  */
+  dispatch_entry = ggc_alloc (sizeof (struct vector_dispatch_spec));
+  dispatch_entry->next = vector_dispatch_list_head;
+  dispatch_entry->target = target_name;
+  dispatch_entry->vector_number = vector_number;
+  vector_dispatch_list_head = dispatch_entry;
+}
+
+/* Function to handle vector attribute.  */
+
+static tree
+mips_vector_attribute (tree *node ATTRIBUTE_UNUSED,
+		       tree name ATTRIBUTE_UNUSED, tree args,
+		       int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  /* If this attribute isn't on the actual function declaration, we
+     ignore it.  */
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    return NULL_TREE;
+
+  /* The vector attribute has a comma delimited list of IRQ #'s as
+     arguments. At least one must be present.  */
+  gcc_assert (args);
+  while (args)
+    {
+      /* The argument must be an integer constant between 0 and 63.  */
+      if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST
+	  || (int) TREE_INT_CST_LOW (TREE_VALUE (args)) < 0
+	  || (int) TREE_INT_CST_LOW (TREE_VALUE (args)) > 63)
+	{
+	  *no_add_attrs = 1;
+	  error ("IRQ number must be an integer between 0 and 63");
+	  return NULL_TREE;
+	}
+
+      /* Add the vector to the list of dispatch functions to emit.  */
+      mips_add_vector_dispatch_entry (IDENTIFIER_POINTER (DECL_NAME (*node)),
+				      (int) TREE_INT_CST_LOW (TREE_VALUE (args)));
+
+      args = TREE_CHAIN (args);
+    }
+
+  return NULL_TREE;
+}
+
 /* Return true if function DECL is a MIPS16 function.  Return the ambient
    setting if DECL is null.  */
 
@@ -6186,6 +6384,11 @@ mips_function_ok_for_sibcall (tree decl,
   if (!TARGET_SIBCALLS)
     return false;
 
+  /* Cannot handle sibcalls from interrupt handler functions since
+     we need extra epilogue code.  */
+  if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
+    return false;
+
   /* We can't do a sibcall if the called function is a MIPS16 function
      because there is no direct "jx" instruction equivalent to "jalx" to
      switch the ISA mode.  We only care about cases where the sibling
@@ -7829,6 +8032,38 @@ mips_file_start (void)
 	     ASM_COMMENT_START,
 	     mips_small_data_threshold, mips_arch_info->name, mips_isa);
 }
+
+/* Implement TARGET_ASM_FILE_END.  */
+
+static void
+mips_file_end (void)
+{
+  struct vector_dispatch_spec *dispatch_entry;
+
+  fputs ("\n# MIPS vector dispatch table\n", asm_out_file);
+
+  /* Output the vector dispatch functions specified in this translation
+     unit, if any.  */
+  for (dispatch_entry = vector_dispatch_list_head; dispatch_entry;
+       dispatch_entry = dispatch_entry->next)
+    {
+      fprintf (asm_out_file, "\t.section\t.vector_%d,\"ax\",@progbits\n",
+	       dispatch_entry->vector_number);
+      fprintf (asm_out_file, "\t.align\t2\n");
+      fprintf (asm_out_file, "\t.set\tnomips16\n");
+      fprintf (asm_out_file, "\t.ent\t__vector_dispatch_%d\n",
+	       dispatch_entry->vector_number);
+      fprintf (asm_out_file, "__vector_dispatch_%d:\n",
+	       dispatch_entry->vector_number);
+      fprintf (asm_out_file, "\tj\t%s\n", dispatch_entry->target);
+      fprintf (asm_out_file, "\t.end\t__vector_dispatch_%d\n",
+	       dispatch_entry->vector_number);
+      fprintf (asm_out_file, "\t.size\t__vector_dispatch_%d, .-"
+	       "__vector_dispatch_%d\n",
+	       dispatch_entry->vector_number,
+	       dispatch_entry->vector_number);
+    }
+}
 \f
 /* Make the last instruction frame-related and note that it performs
    the operation described by FRAME_PATTERN.  */
@@ -8426,6 +8661,22 @@ mips_global_pointer (void)
   return GLOBAL_POINTER_REGNUM;
 }
 
+/* Returns true if regno is a register ordinarilly not callee saved which
+   must nevertheless be preserved by an interrupt handler function.  */
+
+static bool
+mips_register_interrupt_context_p (unsigned int regno)
+{
+  /* return true for v0, v1, a0-a3, t0-t9 and ra  */
+  if ((regno >= 2 && regno <= 15)	/* v0, v1, a0-a3, t0-t7 */
+      || regno == 24			/* t8 */
+      || regno == 25			/* t9 */
+      || regno == 31)			/* ra */
+    return true;
+
+  return false;
+}
+
 /* Return true if the current function must save register REGNO.  */
 
 static bool
@@ -8439,6 +8690,35 @@ mips_save_reg_p (unsigned int regno)
     return true;
 
   /* Check call-saved registers.  */
+  if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+    {
+      /* If this is a leaf function, we can use our analysis of the code
+	 to determine which registers are necessary to save and restore.
+	 If it's not a leaf function, we have to make conservative
+	 assumptions that the called function(s) will tromp all over all of
+	 the call used registers, so we need to save those regardless.  */
+      if (current_function_is_leaf)
+        {
+	  return (crtl->saves_all_registers || df_regs_ever_live_p (regno))
+		 && (!call_really_used_regs[regno]
+		     || mips_register_interrupt_context_p (regno));
+	}
+      else
+        {
+	  /* For non-leaf functions, we save everything we do for normal
+	     functions plus all of the interrupt context.  */
+	  return ((crtl->saves_all_registers || df_regs_ever_live_p (regno))
+		  && !call_really_used_regs[regno])
+		 || mips_register_interrupt_context_p (regno);
+        }
+    }
+  else if (current_function_type == SRS_CONTEXT_SAVE)
+    {
+      /* If we're using a shadow set, we don't need to save any GPR */
+      if (GP_REG_P (regno))
+	return false;
+    }
+
   if ((crtl->saves_all_registers || df_regs_ever_live_p (regno))
       && !call_really_used_regs[regno])
     return true;
@@ -8498,6 +8778,10 @@ mips_save_reg_p (unsigned int regno)
       C |  callee-allocated save area   |
 	|  for register varargs         |
 	|                               |
+	+-------------------------------+ <-- frame_pointer_rtx + int_sp_offset
+	|                               |       + UNITS_PER_WORD
+	|  interrupt save area          |
+	|                               |
 	+-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
 	|                               |       + UNITS_PER_HWFPVALUE
 	|  FPR save area                |
@@ -8541,12 +8825,53 @@ mips_compute_frame_info (void)
   HOST_WIDE_INT offset, size;
   unsigned int regno, i;
 
+  /* Get the current function type.  */
+  if (current_function_type == DONT_KNOW)
+    {
+      tree ipl_tree;
+      if ((ipl_tree = mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
+	  != NULL)
+	{
+	  unsigned int interrupt_priority;
+	  /* The priority can be either "single" or "ipl[0..7]"
+	     When the interrupt handler is for single interrupt mode, we
+	     treat it as a software context save handler. */
+	  if (strcmp (IDENTIFIER_POINTER (TREE_VALUE (ipl_tree)), "single")
+	      == 0)
+	    interrupt_priority = 0;
+	  else
+	    interrupt_priority = IDENTIFIER_POINTER (TREE_VALUE (ipl_tree))[3]
+						 - '0';
+	  if (interrupt_priority == 7)
+	    current_function_type = SRS_CONTEXT_SAVE;
+	  else
+	    current_function_type = SOFTWARE_CONTEXT_SAVE;
+
+	  if (mips_naked_decl_p (current_function_decl))
+	    error ("interrupt handler functions cannot also be naked functions");
+	}
+      else
+	current_function_type = NON_INTERRUPT;
+    }
+
   frame = &cfun->machine->frame;
   memset (frame, 0, sizeof (*frame));
   size = get_frame_size ();
 
   cfun->machine->global_pointer = mips_global_pointer ();
 
+  /* has_interrupt_context_p tells us whether we're saving any interrupt
+     specific data.  */
+  frame->has_interrupt_context_p =
+    (current_function_type == SOFTWARE_CONTEXT_SAVE
+     || current_function_type == SRS_CONTEXT_SAVE);
+
+  /* has_hilo_context_p is true if we need to push/pop HI and LO.  */
+  frame->has_hilo_context_p[0] = false;
+  frame->has_hilo_context_p[1] = false;
+  frame->has_hilo_context_p[2] = false;
+  frame->has_hilo_context_p[3] = false;
+
   /* The first STARTING_FRAME_OFFSET bytes contain the outgoing argument
      area and the $gp save slot.  This area isn't needed in leaf functions,
      but if the target-independent frame size is nonzero, we're committed
@@ -8626,6 +8951,62 @@ mips_compute_frame_info (void)
       frame->fp_sp_offset = offset - UNITS_PER_HWFPVALUE;
     }
 
+  /* Add in space for the interrupt context information.  */
+  if (frame->has_interrupt_context_p)
+    {
+      /* All interrupt context functions need space
+         to preserve STATUS.  */
+      frame->num_interrupt_reg++;
+
+      /* All software interrupt context functions need space
+         to preserve EPC also.  */
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	frame->num_interrupt_reg++;
+
+      /* If HI/LO is defined in this function, we need to save them too.
+         If the function is not a leaf function, we assume that the
+         called function uses them.  */
+      if (!current_function_is_leaf || crtl->saves_all_registers)
+	{
+	  frame->num_interrupt_reg += 2;
+          frame->has_hilo_context_p[0] = true;
+	  if (TARGET_DSP)
+	    {
+	      frame->num_interrupt_reg += 6;
+	      frame->has_hilo_context_p[1] = true;
+	      frame->has_hilo_context_p[2] = true;
+	      frame->has_hilo_context_p[3] = true;
+	    }
+	}
+      else
+	{
+	  unsigned int i;
+	  if (df_regs_ever_live_p (LO_REGNUM)
+	      || df_regs_ever_live_p (HI_REGNUM))
+	    {
+	      frame->num_interrupt_reg += 2;
+	      frame->has_hilo_context_p[0] = true;
+	    }
+
+	  for (i = 2; i < 8; i += 2)
+	    {
+	      if (df_regs_ever_live_p (DSP_ACC_REG_FIRST + i - 2)
+		  || df_regs_ever_live_p (DSP_ACC_REG_FIRST + i - 1))
+	      {
+		frame->num_interrupt_reg += 2;
+		frame->has_hilo_context_p[i >> 1] = true;
+	      }
+	    }
+	}
+    }
+
+  /* Move above the interrupt registers save area.  */
+  if (frame->num_interrupt_reg > 0)
+    {
+      offset += MIPS_STACK_ALIGN (frame->num_interrupt_reg * UNITS_PER_WORD);
+      frame->int_sp_offset = offset - UNITS_PER_WORD;
+    }
+
   /* Move above the callee-allocated varargs save area.  */
   offset += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
   frame->arg_pointer_offset = offset;
@@ -8639,6 +9020,8 @@ mips_compute_frame_info (void)
     frame->gp_save_offset = frame->gp_sp_offset - offset;
   if (frame->fp_sp_offset > 0)
     frame->fp_save_offset = frame->fp_sp_offset - offset;
+  if (frame->num_interrupt_reg > 0)
+    frame->int_save_offset = frame->int_sp_offset - offset;
 
   /* MIPS16 code offsets the frame pointer by the size of the outgoing
      arguments.  This tends to increase the chances of using unextended
@@ -8977,6 +9360,127 @@ mips_output_function_prologue (FILE *fil
      pointer.  This is needed for thunks, since they can use either
      explicit relocs or assembler macros.  */
   mips_output_cplocal ();
+
+  /* If this is an interrupt function, we need to save that
+     context first.  We don't want to use the generic
+     mips_for_each_saved_reg() function because we want to
+     leave defined values in K0 and K1.  */
+  if (cfun->machine->frame.has_interrupt_context_p)
+    {
+      unsigned int i;
+      HOST_WIDE_INT step1;
+      HOST_WIDE_INT offset;
+      HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
+
+      step1 = MIN (tsize, MIPS_MAX_FIRST_STACK_STEP);
+
+      /* If this interrupt is using a shadow register set, we need to
+         get the stack pointer from the previous register set.  */
+      /* Trumping that concern, at least for the time being, is that we
+         want the first four instructions of the interrupt handler to be
+         the same for all handler functions.  This lets there be cache lines
+         locked to those instructions, lowering the latency.  */
+      /* if (current_function_type == SRS_CONTEXT_SAVE) */
+	  fprintf (file, "\trdpgpr\t$sp, $sp\n");
+
+      /* Start at the uppermost location for saving.  */
+      offset = cfun->machine->frame.int_sp_offset;
+
+      /* We want the first four instructions at the top of the interrupt
+	 to be common across all software save handlers so an application
+	 can lock the cache lines to those instructions and reduce the
+	 latency.  */
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+        {
+          /* This is for non-SRS interrupts.  We need to differentiate and
+             generate different code for highest priority handlers.
+             We don't need to move the Cause IPL into the SR for the
+             highest priority interrupt since we're not allowing nested
+             interrupts at that level.  All interrupts are disabled in that
+             case (see below loading of SR), so the IPL value in SR is not
+             relevent.  */
+
+          /* Load the Cause RIPL into k0.  */
+          fprintf (file, "\tmfc0\t$k0, $13\n");
+
+          /* Load EPC into k1.  */
+          fprintf (file, "\tmfc0\t$k1, $14\n");
+        }
+
+      /* Allocate the stack space to save the registers.  If more space
+         needs to be allocated, the expand_prologue() function handled
+         it.  */
+      fprintf (file, "\taddiu\t$sp, $sp, " HOST_WIDE_INT_PRINT_DEC "\n",
+	       -step1);
+
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	{
+	  /* Push EPC into its stack slot.  */
+	  fprintf (file, "\tsw\t$k1, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		   offset);
+	  offset -= GET_MODE_SIZE (SImode);
+	}
+
+      /* Push status into its stack slot.  */
+      fprintf (file, "\tmfc0\t$k1, $12\n");
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	{
+	  /* Right justify the RIPL in k0,
+	     and avoid Read after Write interlock on k1.  */
+          fprintf (file, "\tsrl\t$k0, $k0, %d\n", CAUSE_IPL);
+	}
+
+      fprintf (file, "\tsw\t$k1, " HOST_WIDE_INT_PRINT_DEC "($sp)\n", offset);
+      offset -= GET_MODE_SIZE (SImode);
+
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	{
+	  /* Insert the RIPL into our copy of SR (k1) as the new IPL.  */
+	  fprintf (file, "\tins\t$k1, $k0, %d, 6\n", SR_IPL);
+	}
+
+      /* We may need to also preserve HI/LO. We want to do this as late
+         as possible in the prologue sequence so that if we interrupted
+         during a mul/div operation, we give it as much time to complete
+         as we can before accessing HI/LO and potentially stalling the
+         pipeline.  */
+      for (i = 0; i < 4; i++)
+        if (cfun->machine->frame.has_hilo_context_p[i])
+	  {
+	    if (i == 0)
+	      fprintf (file, "\tmflo\t$k0\n");
+	    else
+	      fprintf (file, "\tmflo\t$k0, $ac%d\n", i);
+	    fprintf (file, "\tsw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		     offset);
+	    offset -= GET_MODE_SIZE (SImode);
+	    if (i == 0)
+	      fprintf (file, "\tmfhi\t$k0\n");
+	    else
+	      fprintf (file, "\tmfhi\t$k0, $ac%d\n", i);
+	    fprintf (file, "\tsw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		     offset);
+	    offset -= GET_MODE_SIZE (SImode);
+	}
+
+      /* Enable interrupts and clear the UM, ERL and EXL bits.
+	 IE is already the correct value, so we don't have to do
+	 anything explicit there for software context save. For SRS,
+	 we're in the highest priority alreadys, so we don't want
+	 to reenable interrupts at all, and should thus also clear IE.  */
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	fprintf (file, "\tins\t$k1, $0, %d, %d\n", SR_EXL, 4);
+      else
+	{
+	  fprintf (file, "\tins\t$k1, $0, %d, %d\n", SR_IE, 5);
+
+	  /* In current IPL7 is the ONLY one with a SRS so we can set the
+	     IPL in Status directly IPL=bits 15-10.  */
+	  fprintf (file, "\tori\t$k1, $k1, 0x1c00\n");
+	}
+
+      fprintf (file, "\tmtc0\t$k1, $12\n");
+    }
 }
 
 /* Implement TARGET_OUTPUT_FUNCTION_EPILOGUE.  */
@@ -8987,6 +9491,72 @@ mips_output_function_epilogue (FILE *fil
 {
   const char *fnname;
 
+  /* If this is an interrupt handler function, we need to process
+     the additional interrupt context as well.  */
+  if (cfun->machine->frame.has_interrupt_context_p)
+    {
+      unsigned int i;
+      HOST_WIDE_INT offset;
+      HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
+      HOST_WIDE_INT step1 = MIN (tsize, MIPS_MAX_FIRST_STACK_STEP);
+
+      /* Disable interrupts.  */
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	{
+	  fprintf (file, "\tdi\n");
+	  fprintf (file, "\tehb\n");
+	}
+
+      /* Restore the original HI/LO if required.  */
+      for (i = 0; i < 4; i++)
+	if (cfun->machine->frame.has_hilo_context_p[i])
+	  {
+	    offset = cfun->machine->frame.int_sp_offset
+		     - GET_MODE_SIZE (SImode);
+
+            if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	      offset -= GET_MODE_SIZE (SImode);
+
+	    fprintf (file, "\tlw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		     offset);
+	    if (i == 0)
+	      fprintf (file, "\tmtlo\t$k0\n");
+	    else
+	      fprintf (file, "\tmtlo\t$k0, $ac%d\n", i);
+	    offset -= GET_MODE_SIZE (SImode);
+	    fprintf (file, "\tlw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		     offset);
+	    if (i == 0)
+	      fprintf (file, "\tmthi\t$k0\n");
+	    else
+	      fprintf (file, "\tmthi\t$k0, $ac%d\n", i);
+	  }
+
+      offset = cfun->machine->frame.int_sp_offset;
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	{
+	  /* Restore the original EPC.  */
+          fprintf (file, "\tlw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n",
+		   offset);
+	  fprintf (file, "\tmtc0\t$k0, $14\n");
+	  offset -= GET_MODE_SIZE (SImode);
+	}
+
+      /* Get the original Status into K0.  */
+      fprintf (file, "\tlw\t$k0, " HOST_WIDE_INT_PRINT_DEC "($sp)\n", offset);
+
+      /* Deallocate the stack space.  */
+      if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+	fprintf (file, "\taddiu\t$sp, $sp, " HOST_WIDE_INT_PRINT_DEC "\n",
+		 step1);
+
+      /* Restore the original status.  */
+      fprintf (file, "\tmtc0\t$k0, $12\n");
+
+      /* Return. */
+      fprintf (file, "\teret\n");
+    }
+
   /* Reinstate the normal $gp.  */
   SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
   mips_output_cplocal ();
@@ -9004,6 +9574,9 @@ mips_output_function_epilogue (FILE *fil
      exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
   mips_end_function_definition (fnname);
+
+  /* Reset current_function_type.  */
+  current_function_type = DONT_KNOW;
 }
 \f
 /* Save register REG to MEM.  Make the instruction frame-related.  */
@@ -9119,13 +9692,19 @@ mips_expand_prologue (void)
   if (cfun->machine->global_pointer > 0)
     SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
 
+  /* If this function is specified as a naked function, just return without
+     expanding any code.  */
+  if (mips_naked_decl_p (current_function_decl))
+    return;
+
   frame = &cfun->machine->frame;
   size = frame->total_size;
 
   /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
      bytes beforehand; this is enough to cover the register save area
      without going out of range.  */
-  if ((frame->mask | frame->fmask) != 0)
+  if (((frame->mask | frame->fmask) != 0)
+      || cfun->machine->frame.has_interrupt_context_p)
     {
       HOST_WIDE_INT step1;
 
@@ -9156,10 +9735,16 @@ mips_expand_prologue (void)
  	}
       else
  	{
-	  insn = gen_add3_insn (stack_pointer_rtx,
-				stack_pointer_rtx,
-				GEN_INT (-step1));
-	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	  /* If this is an interrupt function, this first stack allocation
+	     will be performed by the static frame code and doesn't need
+	     to be done here.  */
+	  if (current_function_type == NON_INTERRUPT)
+	    {
+	      insn = gen_add3_insn (stack_pointer_rtx,
+				    stack_pointer_rtx,
+				    GEN_INT (-step1));
+	      RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	    }
 	  size -= step1;
 	  mips_for_each_saved_reg (size, mips_save_reg);
 	}
@@ -9306,6 +9891,15 @@ mips_expand_epilogue (bool sibcall_p)
       return;
     }
 
+  /* If this function is specified as a naked function, we don't want
+     any actual epilogue code.  We still need to have something for
+     the "return" statement branches to target.  */
+  if (mips_naked_decl_p (current_function_decl))
+    {
+      emit_jump_insn (gen_return_from_epilogue ());
+      return;
+    }
+
   /* In MIPS16 mode, if the return value should go into a floating-point
      register, we need to call a helper routine to copy it over.  */
   if (mips16_cfun_returns_in_fpr_p ())
@@ -9331,7 +9925,8 @@ mips_expand_epilogue (bool sibcall_p)
 
   /* If we need to restore registers, deallocate as much stack as
      possible in the second step without going out of range.  */
-  if ((frame->mask | frame->fmask) != 0)
+  if ((frame->mask | frame->fmask) != 0
+      || cfun->machine->frame.has_interrupt_context_p)
     {
       step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
       step1 -= step2;
@@ -9395,8 +9990,8 @@ mips_expand_epilogue (bool sibcall_p)
       /* Restore the registers.  */
       mips_for_each_saved_reg (frame->total_size - step2, mips_restore_reg);
 
-      /* Deallocate the final bit of the frame.  */
-      if (step2 > 0)
+      /* Deallocate the final bit of the frame for non-interrupts.  */
+      if (!cfun->machine->frame.has_interrupt_context_p && step2 > 0)
 	emit_insn (gen_add3_insn (stack_pointer_rtx,
 				  stack_pointer_rtx,
 				  GEN_INT (step2)));
@@ -9433,7 +10028,19 @@ mips_expand_epilogue (bool sibcall_p)
       else
 	regno = GP_REG_FIRST + 31;
       mips_expand_before_return ();
-      emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
+
+      /* non-interrupt functions generate a return instruction here.  */
+      if (current_function_type == NON_INTERRUPT)
+	emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
+      else
+	{
+	  /* Interrupt functions need to emit a placeholder instruction
+	     so that this pattern will return a non-empty expansion.
+	     We don't want anything below this (there shouldn't be any
+	     RTL following this, anyway) moved above it, so we'll use a
+	     blockage. */
+	  emit_insn (gen_blockage ());
+	}
     }
 }
 \f
@@ -9444,6 +10051,16 @@ mips_expand_epilogue (bool sibcall_p)
 bool
 mips_can_use_return_insn (void)
 {
+  /* Interrupt handlers need to go through the epilogue.  */
+  if (current_function_type != NON_INTERRUPT)
+    return false;
+
+  /* Naked functions don't emit a return instruction directly.
+     since the prologue/epilogue is user-generated, we can't make
+     assumptions about what optimizations might be OK.  */
+  if (mips_naked_decl_p (current_function_decl))
+    return false;
+
   if (!reload_completed)
     return false;
 
@@ -14038,6 +14655,28 @@ mips_order_regs_for_local_alloc (void)
       reg_alloc_order[24] = 0;
     }
 }
+
+/* Implement EPILOGUE_USES.  */
+
+bool
+mips_epilogue_uses (unsigned int regno)
+{
+  /* Say that the epilogue uses the return address register.  Note that
+     in the case of sibcalls, the values "used by the epilogue" are
+     considered live at the start of the called function.
+
+     If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
+     See the comment above load_call<mode> for details.  */
+  if (regno == 31 || (TARGET_USE_GOT && (regno) == GOT_VERSION_REGNUM))
+    return true;
+
+  /* If the register is part of the GPRs that's saved for interrupt context,
+     we need to mark it as used by the epilogue.  */
+  if (current_function_type == SOFTWARE_CONTEXT_SAVE)
+    return mips_register_interrupt_context_p (regno);
+
+  return false;
+}
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
@@ -14112,6 +14751,8 @@ mips_order_regs_for_local_alloc (void)
 
 #undef TARGET_ASM_FILE_START
 #define TARGET_ASM_FILE_START mips_file_start
+#undef TARGET_ASM_FILE_END
+#define TARGET_ASM_FILE_END mips_file_end
 #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
 #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
Index: gcc4x/gcc/gcc/config/mips/mips.md
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.md	2008-10-13 17:29:33.000000000 -0700
+++ gcc4x/gcc/gcc/config/mips/mips.md	2008-10-13 17:33:02.000000000 -0700
@@ -5652,6 +5652,13 @@
   [(set_attr "type"	"jump")
    (set_attr "mode"	"none")])
 
+(define_insn "return_from_epilogue"
+  [(return)]
+  "reload_completed"
+  "%*j\t$31%/"
+  [(set_attr "type"	"jump")
+   (set_attr "mode"	"none")])
+
 ;; Normal return.
 
 (define_insn "return_internal"
Index: gcc4x/gcc/gcc/config/mips/mips-protos.h
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips-protos.h	2008-10-13 17:25:41.000000000 -0700
+++ gcc4x/gcc/gcc/config/mips/mips-protos.h	2008-10-13 17:33:02.000000000 -0700
@@ -330,4 +330,6 @@ extern void mips_expand_atomic_qihi (uni
 
 extern void mips_expand_vector_init (rtx, rtx);
 
+extern bool mips_epilogue_uses (unsigned int);
+
 #endif /* ! GCC_MIPS_PROTOS_H */
Index: gcc4x/gcc/gcc/config/mips/mips.h
===================================================================
--- gcc4x.orig/gcc/gcc/config/mips/mips.h	2008-10-13 17:29:33.000000000 -0700
+++ gcc4x/gcc/gcc/config/mips/mips.h	2008-10-13 17:33:02.000000000 -0700
@@ -1641,6 +1641,13 @@ enum mips_code_readable_setting {
 #define HI_REGNUM	(TARGET_BIG_ENDIAN ? MD_REG_FIRST : MD_REG_FIRST + 1)
 #define LO_REGNUM	(TARGET_BIG_ENDIAN ? MD_REG_FIRST + 1 : MD_REG_FIRST)
 
+/* A few bitfield locations for the coprocessor registers.  */
+#define CAUSE_IPL	10
+#define SR_IPL		10
+#define SR_IE		0
+#define SR_EXL		1
+#define SR_ERL		2
+
 /* FPSW_REGNUM is the single condition code used if !ISA_HAS_8CC.
    If ISA_HAS_8CC, it should not be used, and an arbitrary ST_REG
    should be used instead.  */
@@ -2249,9 +2256,10 @@ typedef struct mips_args {
    considered live at the start of the called function.
 
    If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
-   See the comment above load_call<mode> for details.  */
-#define EPILOGUE_USES(REGNO) \
-  ((REGNO) == 31 || (TARGET_USE_GOT && (REGNO) == GOT_VERSION_REGNUM))
+   See the comment above load_call<mode> for details.
+
+   For interrupt handlers, registers in interrupt context are used.  */
+#define EPILOGUE_USES(REGNO)	(mips_epilogue_uses (REGNO))
 
 /* Treat LOC as a byte offset from the stack pointer and round it up
    to the next fully-aligned offset.  */
Index: gcc4x/gcc/gcc/doc/extend.texi
===================================================================
--- gcc4x.orig/gcc/gcc/doc/extend.texi	2008-10-13 17:25:41.000000000 -0700
+++ gcc4x/gcc/gcc/doc/extend.texi	2008-10-13 17:33:02.000000000 -0700
@@ -2363,7 +2363,7 @@ This attribute is ignored for R8C target
 
 @item interrupt
 @cindex interrupt handler functions
-Use this attribute on the ARM, AVR, CRX, M32C, M32R/D, m68k,
+Use this attribute on the ARM, AVR, CRX, M32C, M32R/D, m68k, MIPS
 and Xstormy16 ports to indicate that the specified function is an
 interrupt handler.  The compiler will generate function entry and exit
 sequences suitable for use in an interrupt handler when this attribute
@@ -2386,6 +2386,56 @@ Permissible values for this parameter ar
 On ARMv7-M the interrupt type is ignored, and the attribute means the function
 may be called with a word aligned stack pointer.
 
+Note, for the MIPS, you must specify the kind of interrupt to be handled by
+adding a parameter to the interrupt attribute like this:
+
+@smallexample
+void f () __attribute__ ((interrupt (single)));
+@end smallexample
+
+The funciton f is a single interrupt handler that deals with all interrupts.
+
+@smallexample
+void g () __attribute__ ((interrupt (ipl6)));
+@end smallexample
+
+The funciton g is an interrupt handler that handles interrupts at the
+priority level of 6.
+
+Permissible values for this parameter are: single, ipl0, ipl1, ipl2, ipl3,
+ipl4, ipl5, ipl6, ipl7.  If ipl7 is used, a shadow register set is
+used for context switch to reduce interrupt latency.  For parameters other
+than ipl7, normal load and store instructions are used for context switch.
+Note that MIPS interrupt handlers assume the stack pointer is valid at all
+time.
+
+@item at_vector
+@cindex install interrupt handler at the address of exception vector on MIPS
+Using this attribute on MIPS indicates that the specific interrupt handler
+should be located at the address of the exception vector (from 0 to 63).
+
+@smallexample
+void f ()  __attribute__ ((interrupt(single))) __attribute__((at_vector (20)));
+@end smallexample
+
+The single interrupt handler f is installed at the exception vectors 20
+which section name is ".vector_20".
+
+@item vector
+@cindex install interrupt dispatch at addresses of exception vectors on MIPS
+Using this attribute on MIPS indicates that the dispatch functions
+should be located at addresses of exception vectors (from 0 to 63)
+which jump to the real interrupt handler.  The number of vectors can be
+more than 1.
+
+@smallexample
+void g ()  __attribute__ ((interrupt(ipl6))) __attribute__((vector (3, 4)));
+@end smallexample
+
+The interrupt handler g has a priority of 6, and is the target from
+dispatch functions at exception vectors 3 (which section name is ".vector_3")
+and 4 (which section name is ".vector_4").
+
 @item interrupt_handler
 @cindex interrupt handler functions on the Blackfin, m68k, H8/300 and SH processors
 Use this attribute on the Blackfin, m68k, H8/300, H8/300H, H8S, and SH to
@@ -2524,7 +2574,7 @@ Note, This feature is currently sorried 
 
 @item naked
 @cindex function without a prologue/epilogue code
-Use this attribute on the ARM, AVR, IP2K and SPU ports to indicate that
+Use this attribute on the ARM, AVR, IP2K, MIPS and SPU ports to indicate that
 the specified function does not need prologue/epilogue sequences generated by
 the compiler.  It is up to the programmer to provide these sequences. The 
 only statements that can be safely included in naked functions are 
Index: gcc4x/gcc/gcc/testsuite/gcc.target/mips/interrupt_handler.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc4x/gcc/gcc/testsuite/gcc.target/mips/interrupt_handler.c	2008-10-14 10:52:47.858502000 -0700
@@ -0,0 +1,30 @@
+/* Test attributes for interrupt handlers */
+/* { dg-do compile } */
+/* { dg-mips-options "-march=mips32r2 -O2" } */
+
+void f () { }
+void __attribute__ ((naked)) n () { }
+
+void __attribute__ ((interrupt(single))) s () { }
+void __attribute__ ((interrupt(ipl0))) v0 () { }
+void __attribute__ ((interrupt(ipl1))) v1 () { }
+void __attribute__ ((interrupt(ipl2))) v2 () { }
+void __attribute__ ((interrupt(ipl3))) v3 () { }
+void __attribute__ ((interrupt(ipl4))) v4 () { }
+void __attribute__ ((interrupt(ipl5))) v5 () { }
+void __attribute__ ((interrupt(ipl5))) v6 () { }
+void __attribute__ ((interrupt(ipl7))) v7 () { }
+
+void __attribute__ ((interrupt(single))) t () { f(); }
+void __attribute__ ((interrupt(ipl0))) u0 () { f(); }
+void __attribute__ ((interrupt(ipl1))) u1 () { f(); }
+void __attribute__ ((interrupt(ipl2))) u2 () { f(); }
+void __attribute__ ((interrupt(ipl3))) u3 () { f(); }
+void __attribute__ ((interrupt(ipl4))) u4 () { f(); }
+void __attribute__ ((interrupt(ipl5))) u5 () { f(); }
+void __attribute__ ((interrupt(ipl6))) u6 () { f(); }
+void __attribute__ ((interrupt(ipl7))) u7 () { f(); }
+
+void __attribute__ ((interrupt(single))) __attribute__ ((at_vector(0))) w () {}
+void __attribute__ ((interrupt(ipl0))) __attribute__ ((at_vector(63))) x () {}
+void __attribute__ ((interrupt(ipl7))) __attribute__ ((vector(1, 2))) y () {}

^ permalink raw reply	[flat|nested] 28+ messages in thread
* RE: [PATCH] MIPS function attributes for interrupt handlers
@ 2008-10-17 11:40 Fu, Chao-Ying
  0 siblings, 0 replies; 28+ messages in thread
From: Fu, Chao-Ying @ 2008-10-17 11:40 UTC (permalink / raw)
  To: Adam Nemet; +Cc: gcc-patches, Lau, David

Nemet wrote:
> > +
> > +      /* If this interrupt is using a shadow register set, 
> we need to
> > +         get the stack pointer from the previous register set.  */
> > +      /* Trumping that concern, at least for the time 
> being, is that we
> > +         want the first four instructions of the interrupt 
> handler to be
> > +         the same for all handler functions.  This lets 
> there be cache lines
> > +         locked to those instructions, lowering the latency.  */
> > +      /* if (current_function_type == SRS_CONTEXT_SAVE) */
> > +	  fprintf (file, "\trdpgpr\t$sp, $sp\n");
> 
> You seems to be using MIPS32r2 instructions in the prologue.  
> I think this should be emitted in RTL.

  Ok.

> It seems that ipl7 implies two things: use the shadow 
> register set and that all interrupts should remain masked.  
> It seems the second can be useful even if the processor does 
> not support shadow registers.  One idea would be to split the 
> two and make them explicit (e.g. use_shadow_register_set and 
> keep_interrupts_masked attributes).
> 

  Yes. You are right.  Single and ipl0-ipl6 generate
same code that doesn't use shadow register sets, and allows nested
interrupts.  Ipl7 uses a shadow register set and doesn't allow
nested interrupts.
Using your two attributes allow a new combination.

> > +@item at_vector
> > +@cindex install interrupt handler at the address of 
> exception vector 
> > +on MIPS Using this attribute on MIPS indicates that the specific 
> > +interrupt handler should be located at the address of the 
> exception 
> > +vector (from 0 to 63).
> > +
> > +@smallexample
> > +void f ()  __attribute__ ((interrupt(single))) 
> > +__attribute__((at_vector (20))); @end smallexample
> > +
> > +The single interrupt handler f is installed at the 
> exception vectors 
> > +20 which section name is ".vector_20".
> 
> I think we shouldn't suggest here that we install this 
> function at a certain address but be clear that all this does 
> is to put the function into a named section.

  Ok.

Regards,
Chao-ying

^ permalink raw reply	[flat|nested] 28+ messages in thread
* Re: [PATCH] MIPS function attributes for interrupt handlers
@ 2008-10-23  9:06 Ross Ridge
  0 siblings, 0 replies; 28+ messages in thread
From: Ross Ridge @ 2008-10-23  9:06 UTC (permalink / raw)
  To: gcc-patches

Mark Mitchell writes:
>It turns out that the GCC manual already says that the only thing you
>can put in a naked function are asm statements without operands.
>
>Given that constraint, I have to adjust my position.  I think that given
>that we've already constrained it that well, we might well allow this on
>all ports.  I don't see any reason why it should be impossible to
>support that on all architectures.  If we don't presently enforce the
>constraint, we certainly can; it's an easy thing for the front-ends to
>check.

I think that's unecessarily too constraining.  I prefer your earlier
suggestion that "any inputs, outputs, clobbers, etc. contain only
references to entities with static storage duration."

In order to support callbacks from an assembly based API where arguments
are passed using abritrary registers and flags, I used the naked
attribute to implement "thunk" wrapper functions.  The thunks where
created by macros that expanded to a naked function containing an asm
statement with a single operand, the callback function being wrapped.
The functions created by the macros were of the form:

	/* extern int callback(int); */

	void __attribute__((naked)) __attribute__((section("_LTEXT"))
	_cbthunk_callback(void) {
		asm volatile("pushl %%edi\n\t"
			     "call %P0\n\t"
			     "addl $4,%%esp\n\t"
			     "negl %%eax\n\t" /* set carry from EAX */
			     "ret" : : "g" (callback));
	}

This saved from me having to know how "callback" was spelt as assembly
name, and gave compile time errors if I mispelt "callback" instead of
link time errors.

I don't see why asm statements in naked functions couldn't support
operands containing either a constant value or entity with a constant
address that's supported in asm statements elsewhere.

					Ross Ridge

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2009-02-28 18:06 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-10-14 20:29 [PATCH] MIPS function attributes for interrupt handlers Fu, Chao-Ying
2008-10-15 23:47 ` Richard Sandiford
2008-10-20 23:03   ` Mark Mitchell
2008-10-20 23:24     ` Adam Nemet
2008-10-21 16:15     ` Weddington, Eric
2008-10-21 17:01       ` Mark Mitchell
2008-10-21 23:11         ` Richard Sandiford
2008-10-21 23:49           ` Mark Mitchell
2008-10-22  0:16             ` Thiemo Seufer
2008-10-22  1:05               ` Mark Mitchell
2008-10-22  6:36                 ` Thiemo Seufer
2008-10-22  6:54                   ` Mark Mitchell
2008-10-22  7:30                     ` Weddington, Eric
2008-10-22 10:03             ` Richard Sandiford
2008-10-22 17:43               ` Mark Mitchell
2008-10-22 20:28                 ` Richard Sandiford
2008-10-28  5:07   ` Fu, Chao-Ying
2008-10-29  8:05     ` Richard Sandiford
2008-10-16 22:34 ` Adam Nemet
2009-02-25  7:01 ` Fu, Chao-Ying
2009-02-25  9:35   ` Adam Nemet
2009-02-25 17:51   ` Daniel Jacobowitz
2009-02-26  9:48     ` Fu, Chao-Ying
2009-02-27 20:46       ` Maciej W. Rozycki
2009-02-28 10:15         ` Fu, Chao-Ying
2009-02-28 18:39           ` Maciej W. Rozycki
2008-10-17 11:40 Fu, Chao-Ying
2008-10-23  9:06 Ross Ridge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).