+Thu Oct 8 01:25:22 1998 Richard Henderson <rth@cygnus.com>
+
+ * flow.c (find_basic_blocks): Calc upper bound for extra nops in
+ max_uids_for_flow.
+ (find_basic_blocks_1): Add a nop to the end of a basic block when
+ a trailing call insn does not have abnormal control flow.
+ * gcse.c (pre_transpout): New variable.
+ (alloc_pre_mem, free_pre_mem, dump_pre_data): Bookkeeping for it.
+ (compute_pre_transpout): Calculate it.
+ (compute_pre_ppinout): Use it to eliminate impossible placements
+ due to abnormal control flow through calls.
+ (compute_pre_data): Call compute_pre_transpout.
+
Wed Oct 7 21:40:24 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sol2-sld-64.h (ASM_CPU_SPEC): Fix typo.
register int i;
rtx nonlocal_label_list = nonlocal_label_rtx_list ();
int in_libcall_block = 0;
+ int extra_uids_for_flow = 0;
/* Count the basic blocks. Also find maximum insn uid value used. */
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
-
/* Track when we are inside in LIBCALL block. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& find_reg_note (insn, REG_LIBCALL, NULL_RTX))
code = GET_CODE (insn);
if (INSN_UID (insn) > max_uid_for_flow)
max_uid_for_flow = INSN_UID (insn);
- if (code == CODE_LABEL
- || (GET_RTX_CLASS (code) == 'i'
- && (prev_code == JUMP_INSN
- || (prev_code == CALL_INSN
- && (nonlocal_label_list != 0 || eh_region))
- || prev_code == BARRIER)))
+ if (code == CODE_LABEL)
i++;
+ else if (GET_RTX_CLASS (code) == 'i')
+ {
+ if (prev_code == JUMP_INSN || prev_code == BARRIER)
+ i++;
+ else if (prev_code == CALL_INSN)
+ {
+ if (nonlocal_label_list != 0 || eh_region)
+ i++;
+ else
+ {
+ /* Else this call does not force a new block to be
+ created. However, it may still be the end of a basic
+ block if it is followed by a CODE_LABEL or a BARRIER.
+
+ To disambiguate calls which force new blocks to be
+ created from those which just happen to be at the end
+ of a block we insert nops during find_basic_blocks_1
+ after calls which are the last insn in a block by
+ chance. We must account for such insns in
+ max_uid_for_flow. */
+
+ extra_uids_for_flow++;
+ }
+ }
+ }
/* We change the code of the CALL_INSN, so that it won't start a
new block. */
These cases are rare, so we don't need too much space. */
max_uid_for_flow += max_uid_for_flow / 10;
#endif
+ max_uid_for_flow += extra_uids_for_flow;
/* Allocate some tables that last till end of compiling this function
and some needed only in find_basic_blocks and life_analysis. */
int depth, pass;
int in_libcall_block = 0;
int deleted_handler = 0;
+ int call_had_abnormal_edge = 0;
pass = 1;
active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
else if (code == CODE_LABEL
|| (GET_RTX_CLASS (code) == 'i'
&& (prev_code == JUMP_INSN
- || (prev_code == CALL_INSN
- && (nonlocal_label_list != 0 || eh_note))
+ || (prev_code == CALL_INSN && call_had_abnormal_edge)
|| prev_code == BARRIER)))
{
basic_block_head[++i] = insn;
if (code == CODE_LABEL)
{
- LABEL_REFS (insn) = insn;
- /* Any label that cannot be deleted
- is considered to start a reachable block. */
- if (LABEL_PRESERVE_P (insn))
- block_live[i] = 1;
- }
+ LABEL_REFS (insn) = insn;
+ /* Any label that cannot be deleted
+ is considered to start a reachable block. */
+ if (LABEL_PRESERVE_P (insn))
+ block_live[i] = 1;
+ }
+
+ /* If the previous insn was a call that did not create an
+ abnormal edge, we want to add a nop so that the CALL_INSN
+ itself is not at basic_block_end. This allows us to easily
+ distinguish between normal calls and those which create
+ abnormal edges in the flow graph. */
+
+ if (i > 0 && !call_had_abnormal_edge
+ && GET_CODE (basic_block_end[i-1]) == CALL_INSN)
+ {
+ rtx nop = gen_rtx_USE (VOIDmode, const0_rtx);
+ nop = emit_insn_after (nop, basic_block_end[i-1]);
+ basic_block_end[i-1] = nop;
+ }
}
else if (GET_RTX_CLASS (code) == 'i')
if (code == CALL_INSN && in_libcall_block)
code = INSN;
+ /* Record whether this call created an edge. */
+ if (code == CALL_INSN)
+ call_had_abnormal_edge = (nonlocal_label_list != 0 || eh_note);
+
if (code != NOTE)
prev_code = code;
static sbitmap *pre_ppin;
static sbitmap *pre_ppout;
+/* Nonzero for expressions that are transparent at the end of the block.
+ This is only zero for expressions killed by abnormal critical edge
+ created by a calls. */
+static sbitmap *pre_transpout;
+
/* Used while performing PRE to denote which insns are redundant. */
static sbitmap pre_redundant;
pre_pavout = sbitmap_vector_alloc (n_blocks, n_exprs);
pre_ppin = sbitmap_vector_alloc (n_blocks, n_exprs);
pre_ppout = sbitmap_vector_alloc (n_blocks, n_exprs);
+
+ pre_transpout = sbitmap_vector_alloc (n_blocks, n_exprs);
}
/* Free vars used for PRE analysis. */
free (pre_transp);
free (pre_comp);
free (pre_antloc);
-
free (pre_avin);
free (pre_avout);
free (pre_antin);
free (pre_pavout);
free (pre_ppin);
free (pre_ppout);
+ free (pre_transpout);
}
/* Dump PRE data. */
pre_ppin, n_basic_blocks);
dump_sbitmap_vector (file, "PRE placement possible on outgoing", "BB",
pre_ppout, n_basic_blocks);
+
+ dump_sbitmap_vector (file, "PRE transparent on outgoing", "BB",
+ pre_transpout, n_basic_blocks);
}
/* Compute the local properties of each recorded expression.
fprintf (gcse_file, "partially avail expr computation: %d passes\n", passes);
}
+/* Compute transparent outgoing information for each block.
+
+ An expression is transparent to an edge unless it is killed by
+ the edge itself. This can only happen with abnormal control flow,
+ when the edge is traversed through a call. This happens with
+ non-local labels and exceptions.
+
+ This would not be necessary if we split the edge. While this is
+ normally impossible for abnormal critical edges, with some effort
+ it should be possible with exception handling, since we still have
+ control over which handler should be invoked. But due to increased
+ EH table sizes, this may not be worthwhile. */
+
+static void
+compute_pre_transpout ()
+{
+ int bb;
+
+ sbitmap_vector_ones (pre_transpout, n_basic_blocks);
+
+ for (bb = 0; bb < n_basic_blocks; ++bb)
+ {
+ int i;
+
+ /* Note that flow inserted a nop a the end of basic blocks that
+ end in call instructions for reasons other than abnormal
+ control flow. */
+ if (GET_CODE (BLOCK_END (bb)) != CALL_INSN)
+ continue;
+
+ for (i = 0; i < expr_hash_table_size; i++)
+ {
+ struct expr *expr;
+ for (expr = expr_hash_table[i]; expr ; expr = expr->next_same_hash)
+ if (GET_CODE (expr->expr) == MEM)
+ {
+ rtx addr = XEXP (expr->expr, 0);
+
+ if (GET_CODE (addr) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (addr))
+ continue;
+
+ /* ??? Optimally, we would use interprocedural alias
+ analysis to determine if this mem is actually killed
+ by this call. */
+ RESET_BIT (pre_transpout[bb], expr->bitmap_index);
+ }
+ }
+ }
+}
+
/* Compute "placement possible" information on entrance and exit of
each block.
for (bb = 0; bb < n_basic_blocks - 1; bb++)
{
sbitmap_ptr ppout = pre_ppout[bb]->elms;
+ sbitmap_ptr transpout = pre_transpout[bb]->elms;
for (i = 0; i < size; i++)
{
int_list_ptr succ;
- SBITMAP_ELT_TYPE tmp = -1L;
+ SBITMAP_ELT_TYPE tmp = *transpout;
for (succ = s_succs[bb]; succ != NULL; succ = succ->next)
{
ppin = pre_ppin[succ_bb]->elms + i;
tmp &= *ppin;
}
+
if (*ppout != tmp)
{
changed = 1;
- *ppout++ = tmp;
+ *ppout = tmp;
}
- else
- ppout++;
+
+ ppout++; transpout++;
}
}
compute_pre_avinout ();
compute_pre_antinout ();
compute_pre_pavinout ();
+ compute_pre_transpout ();
compute_pre_ppinout ();
if (gcse_file)
fprintf (gcse_file, "\n");
}
/* Likewise if the last insn is a call, as will happen in the presence
of exception handling. */
- /* ??? The flag_exceptions test is not exact. We don't know if we are
- actually in an eh region. Fix flow to tell us this. */
- else if (GET_CODE (insn) == CALL_INSN
- && (current_function_has_nonlocal_label || flag_exceptions))
+ else if (GET_CODE (insn) == CALL_INSN)
{
HARD_REG_SET parm_regs;
int nparm_regs;
{
int regno = REGNO (XEXP (XEXP (p, 0), 0));
if (regno >= FIRST_PSEUDO_REGISTER)
- abort();
+ abort ();
SET_HARD_REG_BIT (parm_regs, regno);
nparm_regs++;
}