This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: double max case labels in switch


On Wed, Nov 21, 2001 at 11:53:35PM +1030, Alan Modra wrote:
> This patch doubles the maximum number of case labels that can be
> handled in a switch for those targets that use ADDR_{,DIFF}_VEC and
> have JUMP_TABLES_IN_TEXT_SECTION.  eg. hppa-linux-gcc goes from a
> maximum of 4095 case labels (addr_vec elts are 8 bytes) to 8191.
> Who would write such monster switch statements?  Well, someone sent
> me a bug report regarding some automatically generated code with
> 4500 case labels.
> 
> Note that for targets with instruction sizes a multiple of n bytes,
> we could fairly easily gain another factor of n by scaling values
> stored in insn_lengths.

Ah well, I may as well implement scaling.

Bootstrapped (current 3.0 cvs based) on hppa-linux and powerpc-linux.
Bootstrapped (current 3.1 cvs) i686-linux.

gcc/ChangeLog
	* doc/md.texi (INSNLEN_GCD): Describe.
	* final.c (insn_lengths): Make it unsigned.
	(INSNLEN_GCD): Define.
	(get_attr_length): Scale insn_lengths.
	(align_fuzz): Likewise.
	(insn_current_reference_address): Likewise.
	(shorten_branches): Likewise.  Use an int temp to calculate
	insn_lengths[uid], and improve overflow check.
	* config/pa/pa.h (INSNLEN_GCD): Define.
	* config/rs6000/rs6000.h (INSNLEN_GCD): Define.

-- 
Alan Modra

Index: gcc/doc/md.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/md.texi,v
retrieving revision 1.27
diff -u -p -r1.27 md.texi
--- md.texi	2001/11/14 20:17:08	1.27
+++ md.texi	2001/11/22 10:09:17
@@ -4794,6 +4794,12 @@ When the @code{length} insn attribute is
 value to be assigned to the address of the first insn in a function.  If
 not specified, 0 is used.
 
+@findex INSNLEN_GCD
+@item INSNLEN_GCD
+All machine opcodes are a multiple of INSNLEN_GCD in size.  If not
+specified, 1 is assumed.  This macro scales certain internal
+representations of instruction lengths to allow a larger range.
+
 @findex ADJUST_INSN_LENGTH
 @item ADJUST_INSN_LENGTH (@var{insn}, @var{length})
 If defined, modifies the length assigned to instruction @var{insn} as a
Index: gcc/final.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/final.c,v
retrieving revision 1.222
diff -u -p -r1.222 final.c
--- final.c	2001/11/15 14:58:19	1.222
+++ final.c	2001/11/22 09:43:51
@@ -603,8 +603,14 @@ dbr_sequence_length ()
 /* Arrays for insn lengths, and addresses.  The latter is referenced by
    `insn_current_length'.  */
 
-static short *insn_lengths;
+static unsigned short *insn_lengths;
 
+/* All machine opcodes are a multiple of INSNLEN_GCD in size.  Lengths
+   in INSN_LENGTHS are scaled by this number.  */
+#ifndef INSNLEN_GCD
+#define INSNLEN_GCD 1
+#endif
+
 #ifdef HAVE_ATTR_length
 varray_type insn_addresses_;
 #endif
@@ -679,7 +685,7 @@ get_attr_length (insn)
   int length = 0;
 
   if (insn_lengths_max_uid > INSN_UID (insn))
-    return insn_lengths[INSN_UID (insn)];
+    return insn_lengths[INSN_UID (insn)] * INSNLEN_GCD;
   else
     switch (GET_CODE (insn))
       {
@@ -888,7 +894,7 @@ align_fuzz (start, end, known_align_log,
       int align_addr, new_align;
 
       uid = INSN_UID (align_label);
-      align_addr = INSN_ADDRESSES (uid) - insn_lengths[uid];
+      align_addr = INSN_ADDRESSES (uid) - insn_lengths[uid] * INSNLEN_GCD;
       if (uid_shuid[uid] > end_shuid)
 	break;
       known_align_log = LABEL_TO_ALIGNMENT (align_label);
@@ -939,7 +945,7 @@ insn_current_reference_address (branch)
   if (INSN_SHUID (seq) < INSN_SHUID (dest))
     {
       /* Forward branch.  */
-      return (insn_last_address + insn_lengths[seq_uid]
+      return (insn_last_address + insn_lengths[seq_uid] * INSNLEN_GCD
 	      - align_fuzz (seq, dest, length_unit_log, ~0));
     }
   else
@@ -1186,7 +1192,7 @@ shorten_branches (first)
 #ifdef HAVE_ATTR_length
 
   /* Allocate the rest of the arrays.  */
-  insn_lengths = (short *) xmalloc (max_uid * sizeof (short));
+  insn_lengths = (unsigned short *) xmalloc (max_uid * sizeof (*insn_lengths));
   insn_lengths_max_uid = max_uid;
   /* Syntax errors can lead to labels being outside of the main insn stream.
      Initialize insn_addresses, so that we get reproducible results.  */
@@ -1278,11 +1284,13 @@ shorten_branches (first)
   /* Compute initial lengths, addresses, and varying flags for each insn.  */
   for (insn_current_address = FIRST_INSN_ADDRESS, insn = first;
        insn != 0;
-       insn_current_address += insn_lengths[uid], insn = NEXT_INSN (insn))
+       insn_current_address += insn_lengths[uid] * INSNLEN_GCD,
+	 insn = NEXT_INSN (insn))
     {
+      int i_len;
       uid = INSN_UID (insn);
 
-      insn_lengths[uid] = 0;
+      i_len = 0;
 
       if (GET_CODE (insn) == CODE_LABEL)
 	{
@@ -1291,17 +1299,20 @@ shorten_branches (first)
 	    {
 	      int align = 1 << log;
 	      int new_address = (insn_current_address + align - 1) & -align;
-	      insn_lengths[uid] = new_address - insn_current_address;
+	      i_len = new_address - insn_current_address;
 	    }
 	}
 
       INSN_ADDRESSES (uid) = insn_current_address;
 
-      if (GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER
-	  || GET_CODE (insn) == CODE_LABEL)
-	continue;
-      if (INSN_DELETED_P (insn))
-	continue;
+      if (GET_CODE (insn) == NOTE
+	  || GET_CODE (insn) == BARRIER
+	  || GET_CODE (insn) == CODE_LABEL
+	  || INSN_DELETED_P (insn))
+	{
+	  insn_lengths[uid] = i_len / INSNLEN_GCD;
+	  continue;
+	}
 
       body = PATTERN (insn);
       if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
@@ -1313,13 +1324,12 @@ shorten_branches (first)
 	      || 1
 #endif
 	      )
-	    insn_lengths[uid] = (XVECLEN (body,
-					  GET_CODE (body) == ADDR_DIFF_VEC)
-				 * GET_MODE_SIZE (GET_MODE (body)));
+	    i_len = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
+		     * GET_MODE_SIZE (GET_MODE (body)));
 	  /* Alignment is handled by ADDR_VEC_ALIGN.  */
 	}
       else if (GET_CODE (body) == ASM_INPUT || asm_noperands (body) >= 0)
-	insn_lengths[uid] = asm_insn_count (body) * insn_default_length (insn);
+	i_len = asm_insn_count (body) * insn_default_length (insn);
       else if (GET_CODE (body) == SEQUENCE)
 	{
 	  int i;
@@ -1345,32 +1355,33 @@ shorten_branches (first)
 	      else
 		inner_length = insn_default_length (inner_insn);
 
-	      insn_lengths[inner_uid] = inner_length;
+	      insn_lengths[inner_uid] = inner_length / INSNLEN_GCD;
 	      if (const_delay_slots)
 		{
 		  if ((varying_length[inner_uid]
 		       = insn_variable_length_p (inner_insn)) != 0)
 		    varying_length[uid] = 1;
-		  INSN_ADDRESSES (inner_uid) = (insn_current_address
-						+ insn_lengths[uid]);
+		  INSN_ADDRESSES (inner_uid) = insn_current_address + i_len;
 		}
 	      else
 		varying_length[inner_uid] = 0;
-	      insn_lengths[uid] += inner_length;
+	      i_len += inner_length;
 	    }
 	}
       else if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER)
 	{
-	  insn_lengths[uid] = insn_default_length (insn);
+	  i_len = insn_default_length (insn);
 	  varying_length[uid] = insn_variable_length_p (insn);
 	}
 
       /* If needed, do any adjustment.  */
 #ifdef ADJUST_INSN_LENGTH
-      ADJUST_INSN_LENGTH (insn, insn_lengths[uid]);
-      if (insn_lengths[uid] < 0)
-	fatal_insn ("Negative insn length", insn);
+      ADJUST_INSN_LENGTH (insn, i_len);
 #endif
+      if (i_len < 0
+	  || (unsigned short) (i_len / INSNLEN_GCD) != i_len / INSNLEN_GCD)
+	fatal_insn ("Overflowed insn length", insn);
+      insn_lengths[uid] = i_len / INSNLEN_GCD;
     }
 
   /* Now loop over all the insns finding varying length insns.  For each,
@@ -1400,7 +1411,8 @@ shorten_branches (first)
 		{
 		  int align = 1 << log;
 		  int new_address= (insn_current_address + align - 1) & -align;
-		  insn_lengths[uid] = new_address - insn_current_address;
+		  insn_lengths[uid] = ((new_address - insn_current_address)
+				       / INSNLEN_GCD);
 		  insn_current_align = log;
 		  insn_current_address = new_address;
 		}
@@ -1422,7 +1434,7 @@ shorten_branches (first)
 	      && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
 	    {
 	      rtx body = PATTERN (insn);
-	      int old_length = insn_lengths[uid];
+	      int old_length = insn_lengths[uid] * INSNLEN_GCD;
 	      rtx rel_lab = XEXP (XEXP (body, 0), 0);
 	      rtx min_lab = XEXP (XEXP (body, 2), 0);
 	      rtx max_lab = XEXP (XEXP (body, 3), 0);
@@ -1519,10 +1531,11 @@ shorten_branches (first)
 #endif
 		  )
 		{
-		  insn_lengths[uid]
-		    = (XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body)));
-		  insn_current_address += insn_lengths[uid];
-		  if (insn_lengths[uid] != old_length)
+		  int new_len = (XVECLEN (body, 1)
+				 * GET_MODE_SIZE (GET_MODE (body)));
+		  insn_lengths[uid] = new_len / INSNLEN_GCD;
+		  insn_current_address += new_len;
+		  if (new_len != old_length)
 		    something_changed = 1;
 		}
 
@@ -1545,11 +1558,12 @@ shorten_branches (first)
 
 		      INSN_ADDRESSES (inner_uid) = insn_current_address;
 
-		      insn_current_address += insn_lengths[inner_uid];
+		      insn_current_address += (insn_lengths[inner_uid]
+					       * INSNLEN_GCD);
 		    }
                 }
 	      else
-		insn_current_address += insn_lengths[uid];
+		insn_current_address += insn_lengths[uid] * INSNLEN_GCD;
 
 	      continue;
 	    }
@@ -1571,16 +1585,18 @@ shorten_branches (first)
 		  /* insn_current_length returns 0 for insns with a
 		     non-varying length.  */
 		  if (! varying_length[inner_uid])
-		    inner_length = insn_lengths[inner_uid];
+		    inner_length = insn_lengths[inner_uid] * INSNLEN_GCD;
 		  else
-		    inner_length = insn_current_length (inner_insn);
-
-		  if (inner_length != insn_lengths[inner_uid])
 		    {
-		      insn_lengths[inner_uid] = inner_length;
-		      something_changed = 1;
+		      inner_length = insn_current_length (inner_insn);
+
+		      if (inner_length / INSNLEN_GCD != insn_lengths[inner_uid])
+			{
+			  insn_lengths[inner_uid] = inner_length / INSNLEN_GCD;
+			  something_changed = 1;
+			}
 		    }
-		  insn_current_address += insn_lengths[inner_uid];
+		  insn_current_address += inner_length;
 		  new_length += inner_length;
 		}
 	    }
@@ -1597,9 +1613,9 @@ shorten_branches (first)
 	  insn_current_address += (new_length - tmp_length);
 #endif
 
-	  if (new_length != insn_lengths[uid])
+	  if (new_length / INSNLEN_GCD != insn_lengths[uid])
 	    {
-	      insn_lengths[uid] = new_length;
+	      insn_lengths[uid] = new_length / INSNLEN_GCD;
 	      something_changed = 1;
 	    }
 	}
Index: gcc/config/pa/pa.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/pa/pa.h,v
retrieving revision 1.129
diff -u -p -r1.129 pa.h
--- pa.h	2001/11/11 17:45:02	1.129
+++ pa.h	2001/11/22 09:56:08
@@ -1666,6 +1666,9 @@ while (0)
 
 /* Adjust the cost of branches.  */
 #define BRANCH_COST (pa_cpu == PROCESSOR_8000 ? 2 : 1)
+
+/* All machine opcodes are a multiple of INSNLEN_GCD in size.  */
+#define INSNLEN_GCD 4
 
 /* Handling the special cases is going to get too complicated for a macro,
    just call `pa_adjust_insn_length' to do the real work.  */
Index: gcc/config/rs6000/rs6000.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000.h,v
retrieving revision 1.140
diff -u -p -r1.140 rs6000.h
--- rs6000.h	2001/11/20 19:43:28	1.140
+++ rs6000.h	2001/11/22 09:56:12
@@ -2336,6 +2336,9 @@ do {									     \
 
 #define ADDRESS_COST(RTX) 0
 
+/* All machine opcodes are a multiple of INSNLEN_GCD in size.  */
+#define INSNLEN_GCD 4
+
 /* Adjust the length of an INSN.  LENGTH is the currently-computed length and
    should be adjusted to reflect any required changes.  This macro is used when
    there is some systematic length adjustment required that would be difficult


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]