This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: double max case labels in switch
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 22 Nov 2001 20:58:30 +1030
- Subject: Re: double max case labels in switch
- References: <20011121235335.P6922@bubble.sa.bigpond.net.au>
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