varasm.o : varasm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(RTL_H) \
$(FLAGS_H) function.h $(EXPR_H) hard-reg-set.h $(REGS_H) \
output.h $(C_PRAGMA_H) toplev.h xcoffout.h debug.h $(GGC_H) $(TM_P_H) \
- $(HASHTAB_H) $(TARGET_H) langhooks.h gt-varasm.h real.h
+ $(HASHTAB_H) $(TARGET_H) langhooks.h gt-varasm.h real.h $(BASIC_BLOCK_H)
function.o : function.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(TREE_H) $(CFGLAYOUT_H) $(TREE_GIMPLE_H) \
$(FLAGS_H) function.h $(EXPR_H) $(OPTABS_H) libfuncs.h $(REGS_H) hard-reg-set.h \
$(RTL_H) $(GGC_H) gt-lists.h
bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(BASIC_BLOCK_H) $(FLAGS_H) $(TIMEVAR_H) output.h $(CFGLAYOUT_H) $(FIBHEAP_H) \
- $(TARGET_H) function.h $(TM_P_H) $(OBSTACK_H) $(EXPR_H) $(REGS_H)
+ $(TARGET_H) function.h $(TM_P_H) $(OBSTACK_H) $(EXPR_H) $(REGS_H) errors.h
tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
$(BASIC_BLOCK_H) hard-reg-set.h output.h $(CFGLAYOUT_H) $(FLAGS_H) $(TIMEVAR_H) \
$(PARAMS_H) $(COVERAGE_H)
# Files never linked into the final executable produces warnings about missing
# profile.
STAGEFEEDBACK_FLAGS_TO_PASS = \
- CFLAGS="$(BOOT_CFLAGS) -fprofile-use"
+ CFLAGS="$(BOOT_CFLAGS) -fprofile-use -freorder-blocks-and-partition"
# Only build the C compiler for stage1, because that is the only one that
# we can guarantee will build with the native compiler, and also it is the
#include "tm_p.h"
#include "obstack.h"
#include "expr.h"
+#include "errors.h"
#include "params.h"
/* The number of rounds. In most cases there will only be 4 rounds, but
/* Which trace is the bb end of (-1 means it is not an end of a trace). */
int end_of_trace;
+ /* Which trace is the bb in? */
+ int in_trace;
+
/* Which heap is BB in (if any)? */
fibheap_t heap;
static bool copy_bb_p (basic_block, int);
static int get_uncond_jump_length (void);
static bool push_to_next_round_p (basic_block, int, int, int, gcov_type);
-static void add_unlikely_executed_notes (void);
static void find_rarely_executed_basic_blocks_and_crossing_edges (edge *,
int *,
int *);
-static void mark_bb_for_unlikely_executed_section (basic_block);
static void add_labels_and_missing_jumps (edge *, int);
static void add_reg_crossing_jump_notes (void);
static void fix_up_fall_thru_edges (void);
int exec_th, gcov_type count_th)
{
bool there_exists_another_round;
- bool cold_block;
bool block_not_hot_enough;
- bool next_round_is_last;
there_exists_another_round = round < number_of_rounds - 1;
- next_round_is_last = round + 1 == number_of_rounds - 1;
-
- cold_block = (flag_reorder_blocks_and_partition
- && BB_PARTITION (bb) == BB_COLD_PARTITION);
block_not_hot_enough = (bb->frequency < exec_th
|| bb->count < count_th
|| probably_never_executed_bb_p (bb));
- if (flag_reorder_blocks_and_partition
- && next_round_is_last
- && BB_PARTITION (bb) != BB_COLD_PARTITION)
- return false;
- else if (there_exists_another_round
- && (cold_block || block_not_hot_enough))
+ if (there_exists_another_round
+ && block_not_hot_enough)
return true;
else
return false;
cold blocks (and ONLY the cold blocks). */
number_of_rounds = N_ROUNDS - 1;
- if (flag_reorder_blocks_and_partition)
- number_of_rounds = N_ROUNDS;
/* Insert entry points of function into heap. */
heap = fibheap_new ();
struct trace *traces, int *n_traces, int round,
fibheap_t *heap, int number_of_rounds)
{
- /* The following variable refers to the last round in which non-"cold"
- blocks may be collected into a trace. */
-
- int last_round = N_ROUNDS - 1;
-
/* Heap for discarded basic blocks which are possible starting points for
the next round. */
fibheap_t new_heap = fibheap_new ();
trace->first = bb;
trace->round = round;
trace->length = 0;
+ bbd[bb->index].in_trace = *n_traces;
(*n_traces)++;
do
&& e->dest->rbi->visited != *n_traces)
continue;
- if (BB_PARTITION (e->dest) == BB_COLD_PARTITION
- && round < last_round)
+ if (BB_PARTITION (e->dest) != BB_PARTITION (bb))
continue;
prob = e->probability;
best_edge->dest->index, bb->index);
}
bb->rbi->next = best_edge->dest;
+ bbd[best_edge->dest->index].in_trace =
+ (*n_traces) - 1;
bb = rotate_loop (best_edge, trace, *n_traces);
}
}
{
bb = copy_bb (best_edge->dest, best_edge, bb,
*n_traces);
+ trace->length++;
}
}
}
&& !e->dest->rbi->visited
&& single_pred_p (e->dest)
&& !(e->flags & EDGE_CROSSING)
- && single_succ_p (e->dest) == 1
+ && single_succ_p (e->dest)
&& (single_succ_edge (e->dest)->flags
& EDGE_CAN_FALLTHRU)
&& !(single_succ_edge (e->dest)->flags & EDGE_COMPLEX)
}
bb->rbi->next = best_edge->dest;
+ bbd[best_edge->dest->index].in_trace = (*n_traces) - 1;
bb = best_edge->dest;
}
}
for (i = array_size; i < new_size; i++)
{
bbd[i].start_of_trace = -1;
+ bbd[i].in_trace = -1;
bbd[i].end_of_trace = -1;
bbd[i].heap = NULL;
bbd[i].node = NULL;
}
}
+ bbd[new_bb->index].in_trace = trace;
+
return new_bb;
}
connect_traces (int n_traces, struct trace *traces)
{
int i;
- int unconnected_hot_trace_count = 0;
- bool cold_connected = true;
bool *connected;
- bool *cold_traces;
+ bool two_passes;
int last_trace;
+ int current_pass;
+ int current_partition;
int freq_threshold;
gcov_type count_threshold;
connected = xcalloc (n_traces, sizeof (bool));
last_trace = -1;
-
- /* If we are partitioning hot/cold basic blocks, mark the cold
- traces as already connected, to remove them from consideration
- for connection to the hot traces. After the hot traces have all
- been connected (determined by "unconnected_hot_trace_count"), we
- will go back and connect the cold traces. */
-
- cold_traces = xcalloc (n_traces, sizeof (bool));
+ current_pass = 1;
+ current_partition = BB_PARTITION (traces[0].first);
+ two_passes = false;
if (flag_reorder_blocks_and_partition)
- for (i = 0; i < n_traces; i++)
- {
- if (BB_PARTITION (traces[i].first) == BB_COLD_PARTITION)
- {
- connected[i] = true;
- cold_traces[i] = true;
- cold_connected = false;
- }
- else
- unconnected_hot_trace_count++;
- }
-
- for (i = 0; i < n_traces || !cold_connected ; i++)
+ for (i = 0; i < n_traces && !two_passes; i++)
+ if (BB_PARTITION (traces[0].first)
+ != BB_PARTITION (traces[i].first))
+ two_passes = true;
+
+ for (i = 0; i < n_traces || (two_passes && current_pass == 1) ; i++)
{
int t = i;
int t2;
edge e, best;
int best_len;
- /* If we are partitioning hot/cold basic blocks, check to see
- if all the hot traces have been connected. If so, go back
- and mark the cold traces as unconnected so we can connect
- them up too. Re-set "i" to the first (unconnected) cold
- trace. Use flag "cold_connected" to make sure we don't do
- this step more than once. */
-
- if (flag_reorder_blocks_and_partition
- && (i >= n_traces || unconnected_hot_trace_count <= 0)
- && !cold_connected)
+ if (i >= n_traces)
{
- int j;
- int first_cold_trace = -1;
-
- for (j = 0; j < n_traces; j++)
- if (cold_traces[j])
- {
- connected[j] = false;
- if (first_cold_trace == -1)
- first_cold_trace = j;
- }
- i = t = first_cold_trace;
- cold_connected = true;
+ if (two_passes && current_pass == 1)
+ {
+ i = 0;
+ t = i;
+ current_pass = 2;
+ if (current_partition == BB_HOT_PARTITION)
+ current_partition = BB_COLD_PARTITION;
+ else
+ current_partition = BB_HOT_PARTITION;
+ }
+ else
+ abort ();
}
-
+
if (connected[t])
continue;
+ if (two_passes
+ && BB_PARTITION (traces[t].first) != current_partition)
+ continue;
+
connected[t] = true;
- if (unconnected_hot_trace_count > 0)
- unconnected_hot_trace_count--;
/* Find the predecessor traces. */
for (t2 = t; t2 > 0;)
&& !(e->flags & EDGE_COMPLEX)
&& bbd[si].end_of_trace >= 0
&& !connected[bbd[si].end_of_trace]
+ && (BB_PARTITION (e->src) == current_partition)
&& (!best
|| e->probability > best->probability
|| (e->probability == best->probability
t2 = bbd[best->src->index].end_of_trace;
connected[t2] = true;
- if (unconnected_hot_trace_count > 0)
- unconnected_hot_trace_count--;
-
if (dump_file)
{
fprintf (dump_file, "Connection: %d %d\n",
&& !(e->flags & EDGE_COMPLEX)
&& bbd[di].start_of_trace >= 0
&& !connected[bbd[di].start_of_trace]
+ && (BB_PARTITION (e->dest) == current_partition)
&& (!best
|| e->probability > best->probability
|| (e->probability == best->probability
t = bbd[best->dest->index].start_of_trace;
traces[last_trace].last->rbi->next = traces[t].first;
connected[t] = true;
- if (unconnected_hot_trace_count > 0)
- unconnected_hot_trace_count--;
last_trace = t;
}
else
&& !(e2->flags & EDGE_COMPLEX)
&& bbd[di].start_of_trace >= 0
&& !connected[bbd[di].start_of_trace]
+ && (BB_PARTITION (e2->dest) == current_partition)
&& (EDGE_FREQUENCY (e2) >= freq_threshold)
&& (e2->count >= count_threshold)
&& (!best2
t = bbd[next_bb->index].start_of_trace;
traces[last_trace].last->rbi->next = traces[t].first;
connected[t] = true;
- if (unconnected_hot_trace_count > 0)
- unconnected_hot_trace_count--;
last_trace = t;
}
else
}
FREE (connected);
- FREE (cold_traces);
}
/* Return true when BB can and should be copied. CODE_MAY_GROW is true
return length;
}
-static void
-add_unlikely_executed_notes (void)
-{
- basic_block bb;
-
- /* Add the UNLIKELY_EXECUTED_NOTES to each cold basic block. */
-
- FOR_EACH_BB (bb)
- if (BB_PARTITION (bb) == BB_COLD_PARTITION)
- mark_bb_for_unlikely_executed_section (bb);
-}
-
/* Find the basic blocks that are rarely executed and need to be moved to
a separate section of the .o file (to cut down on paging and improve
cache locality). */
}
}
- /* Since all "hot" basic blocks will eventually be scheduled before all
- cold basic blocks, make *sure* the real function entry block is in
- the hot partition (if there is one). */
-
- if (has_hot_blocks)
- FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
- if (e->dest->index >= 0)
- {
- BB_SET_PARTITION (e->dest, BB_HOT_PARTITION);
- break;
- }
-
/* Mark every edge that crosses between sections. */
i = 0;
*n_crossing_edges = i;
}
-/* Add NOTE_INSN_UNLIKELY_EXECUTED_CODE to top of basic block. This note
- is later used to mark the basic block to be put in the
- unlikely-to-be-executed section of the .o file. */
-
-static void
-mark_bb_for_unlikely_executed_section (basic_block bb)
-{
- rtx cur_insn;
- rtx insert_insn = NULL;
- rtx new_note;
-
- /* Insert new NOTE immediately after BASIC_BLOCK note. */
-
- for (cur_insn = BB_HEAD (bb); cur_insn != NEXT_INSN (BB_END (bb));
- cur_insn = NEXT_INSN (cur_insn))
- if (GET_CODE (cur_insn) == NOTE
- && NOTE_LINE_NUMBER (cur_insn) == NOTE_INSN_BASIC_BLOCK)
- {
- insert_insn = cur_insn;
- break;
- }
-
- /* If basic block does not contain a NOTE_INSN_BASIC_BLOCK, there is
- a major problem. */
- gcc_assert (insert_insn);
-
- /* Insert note and assign basic block number to it. */
-
- new_note = emit_note_after (NOTE_INSN_UNLIKELY_EXECUTED_CODE,
- insert_insn);
- NOTE_BASIC_BLOCK (new_note) = bb;
-}
-
/* If any destination of a crossing edge does not have a label, add label;
Convert any fall-through crossing edges (for blocks that do not contain
a jump) to unconditional jumps. */
FOR_EACH_BB (cur_bb)
{
last_insn = BB_END (cur_bb);
+
+ if (EDGE_COUNT (cur_bb->succs) < 1)
+ continue;
+
succ = EDGE_SUCC (cur_bb, 0);
/* Check to see if bb ends in a crossing (unconditional) jump. At
(e->src)));
}
-/* Basic blocks containing NOTE_INSN_UNLIKELY_EXECUTED_CODE will be
- put in a separate section of the .o file, to reduce paging and
- improve cache performance (hopefully). This can result in bits of
- code from the same function being widely separated in the .o file.
- However this is not obvious to the current bb structure. Therefore
- we must take care to ensure that: 1). There are no fall_thru edges
- that cross between sections; 2). For those architectures which
- have "short" conditional branches, all conditional branches that
- attempt to cross between sections are converted to unconditional
- branches; and, 3). For those architectures which have "short"
- unconditional branches, all unconditional branches that attempt
- to cross between sections are converted to indirect jumps.
-
+/* Hot and cold basic blocks are partitioneed and put in separate
+ sections of the .o file, to reduce paging and improve cache
+ performance (hopefully). This can result in bits of code from the
+ same function being widely separated in the .o file. However this
+ is not obvious to the current bb structure. Therefore we must take
+ care to ensure that: 1). There are no fall_thru edges that cross
+ between sections; 2). For those architectures which have "short"
+ conditional branches, all conditional branches that attempt to
+ cross between sections are converted to unconditional branches;
+ and, 3). For those architectures which have "short" unconditional
+ branches, all unconditional branches that attempt to cross between
+ sections are converted to indirect jumps.
+
The code for fixing up fall_thru edges that cross between hot and
cold basic blocks does so by creating new basic blocks containing
unconditional branches to the appropriate label in the "other"
}
}
+/* Verify, in the basic block chain, that there is at most one switch
+ between hot/cold partitions. This is modelled on
+ rtl_verify_flow_info_1, but it cannot go inside that function
+ because this condition will not be true until after
+ reorder_basic_blocks is called. */
+
+static void
+verify_hot_cold_block_grouping (void)
+{
+ basic_block bb;
+ int err = 0;
+ bool switched_sections = false;
+ int current_partition = 0;
+
+ FOR_EACH_BB (bb)
+ {
+ if (!current_partition)
+ current_partition = BB_PARTITION (bb);
+ if (BB_PARTITION (bb) != current_partition)
+ {
+ if (switched_sections)
+ {
+ error ("Multiple hot/cold transitions found (bb %i)",
+ bb->index);
+ err = 1;
+ }
+ else
+ {
+ switched_sections = true;
+ current_partition = BB_PARTITION (bb);
+ }
+ }
+ }
+
+ if (err)
+ internal_error ("verify_hot_cold_block_grouping failed");
+}
+
/* Reorder basic blocks. The main entry point to this file. FLAGS is
the set of flags to pass to cfg_layout_initialize(). */
for (i = 0; i < array_size; i++)
{
bbd[i].start_of_trace = -1;
+ bbd[i].in_trace = -1;
bbd[i].end_of_trace = -1;
bbd[i].heap = NULL;
bbd[i].node = NULL;
if (dump_file)
dump_flow_info (dump_file);
- if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections)
- add_unlikely_executed_notes ();
-
cfg_layout_finalize ();
+ verify_hot_cold_block_grouping ();
timevar_pop (TV_REORDER_BLOCKS);
}
+/* Determine which partition the first basic block in the function
+ belongs to, then find the first basic block in the current function
+ that belongs to a different section, and insert a
+ NOTE_INSN_SWITCH_TEXT_SECTIONS note immediately before it in the
+ instruction stream. When writing out the assembly code,
+ encountering this note will make the compiler switch between the
+ hot and cold text sections. */
+
+void
+insert_section_boundary_note (void)
+{
+ basic_block bb;
+ rtx new_note;
+ int first_partition = 0;
+
+ if (flag_reorder_blocks_and_partition
+ && targetm.have_named_sections)
+ FOR_EACH_BB (bb)
+ {
+ if (!first_partition)
+ first_partition = BB_PARTITION (bb);
+ if (BB_PARTITION (bb) != first_partition)
+ {
+ new_note = emit_note_before (NOTE_INSN_SWITCH_TEXT_SECTIONS,
+ BB_HEAD (bb));
+ break;
+ }
+ }
+}
+
/* Duplicate the blocks containing computed gotos. This basically unfactors
computed gotos that were factored early on in the compilation process to
speed up edge based data flow. We used to not unfactoring them again,
/* If the block ends in a computed jump and it is small enough,
make it a candidate for duplication. */
- if (computed_jump_p (BB_END (bb)))
+ if (computed_jump_p (BB_END (bb))
+ && !find_reg_note (BB_END (bb), REG_CROSSING_JUMP, NULL_RTX))
{
rtx insn;
int size = 0;
function above).
This optimization checks the feedback information to determine
- which basic blocks are hot/cold and causes reorder_basic_blocks to
- add NOTE_INSN_UNLIKELY_EXECUTED_CODE to non-hot basic blocks. The
- presence or absence of this note is later used for writing out
- sections in the .o file. Because hot and cold sections can be
- arbitrarily large (within the bounds of memory), far beyond the
- size of a single function, it is necessary to fix up all edges that
- cross section boundaries, to make sure the instructions used can
- actually span the required distance. The fixes are described
- below.
+ which basic blocks are hot/cold, updates flags on the basic blocks
+ to indicate which section they belong in. This information is
+ later used for writing out sections in the .o file. Because hot
+ and cold sections can be arbitrarily large (within the bounds of
+ memory), far beyond the size of a single function, it is necessary
+ to fix up all edges that cross section boundaries, to make sure the
+ instructions used can actually span the required distance. The
+ fixes are described below.
Fall-through edges must be changed into jumps; it is not safe or
legal to fall through across a section boundary. Whenever a
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (BB_PARTITION (jump_block) != BB_PARTITION (jump_dest_block)
- || (cbranch_jump_edge->flags & EDGE_CROSSING)))
+ if (BB_PARTITION (jump_block) != BB_PARTITION (jump_dest_block)
+ || (cbranch_jump_edge->flags & EDGE_CROSSING))
return false;
/* The conditional branch must target the block after the
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX))
+ if (find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX))
return false;
for (ei = ei_start (b->succs); (e = ei_safe_edge (ei)); )
bb-reorder.c:partition_hot_cold_basic_blocks for complete
details. */
- if (flag_reorder_blocks_and_partition
- && first != EXIT_BLOCK_PTR
+ if (first != EXIT_BLOCK_PTR
&& find_reg_note (BB_END (first), REG_CROSSING_JUMP, NULL_RTX))
return false;
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (BB_PARTITION (a) != BB_PARTITION (b)
- || find_reg_note (BB_END (a), REG_CROSSING_JUMP, NULL_RTX)))
+ if (BB_PARTITION (a) != BB_PARTITION (b))
return;
barrier = next_nonnote_insn (BB_END (a));
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (find_reg_note (BB_END (a), REG_CROSSING_JUMP, NULL_RTX)
- || BB_PARTITION (a) != BB_PARTITION (b)))
+ if (BB_PARTITION (a) != BB_PARTITION (b))
return;
real_b_end = BB_END (b);
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX)
- || find_reg_note (BB_END (c), REG_CROSSING_JUMP, NULL_RTX)
- || BB_PARTITION (b) != BB_PARTITION (c)))
+ if (BB_PARTITION (b) != BB_PARTITION (c))
return NULL;
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (BB_PARTITION (EDGE_PRED (bb, 0)->src) != BB_PARTITION (EDGE_PRED (bb, 1)->src)
- || (EDGE_PRED (bb, 0)->flags & EDGE_CROSSING)))
+ if (BB_PARTITION (EDGE_PRED (bb, 0)->src) !=
+ BB_PARTITION (EDGE_PRED (bb, 1)->src)
+ || (EDGE_PRED (bb, 0)->flags & EDGE_CROSSING))
return false;
/* It is always cheapest to redirect a block that ends in a branch to
void verify_insn_chain (void);
static void fixup_fallthru_exit_predecessor (void);
static tree insn_scope (rtx);
-static void update_unlikely_executed_notes (basic_block);
\f
rtx
unlink_insn_chain (rtx first, rtx last)
section boundaries). */
BB_COPY_PARTITION (e_fall->src, single_pred (bb));
if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections)
- {
- if (BB_PARTITION (single_pred (bb)) == BB_COLD_PARTITION)
- {
- rtx new_note;
- rtx note = BB_HEAD (e_fall->src);
-
- while (!INSN_P (note)
- && note != BB_END (e_fall->src))
- note = NEXT_INSN (note);
-
- new_note = emit_note_before
- (NOTE_INSN_UNLIKELY_EXECUTED_CODE,
- note);
- NOTE_BASIC_BLOCK (new_note) = bb;
- }
- if (JUMP_P (BB_END (bb))
- && !any_condjump_p (BB_END (bb))
- && (single_succ_edge (bb)->flags & EDGE_CROSSING))
- REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
- (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
- }
+ && targetm.have_named_sections
+ && JUMP_P (BB_END (bb))
+ && !any_condjump_p (BB_END (bb))
+ && (EDGE_SUCC (bb, 0)->flags & EDGE_CROSSING))
+ REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
+ (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
}
}
bb->index = index;
BASIC_BLOCK (index) = bb;
- update_unlikely_executed_notes (bb);
-
bb->prev_bb = prev_bb;
prev_bb->next_bb = bb;
}
}
}
\f
-/* Update the basic block number information in any
- NOTE_INSN_UNLIKELY_EXECUTED_CODE notes within the basic block. */
-
-static void
-update_unlikely_executed_notes (basic_block bb)
-{
- rtx cur_insn;
-
- for (cur_insn = BB_HEAD (bb); cur_insn != BB_END (bb);
- cur_insn = NEXT_INSN (cur_insn))
- if (NOTE_P (cur_insn)
- && NOTE_LINE_NUMBER (cur_insn) == NOTE_INSN_UNLIKELY_EXECUTED_CODE)
- NOTE_BASIC_BLOCK (cur_insn) = bb;
-}
-\f
/* Perform sanity checks on the insn chain.
1. Check that next/prev pointers are consistent in both the forward and
reverse direction.
break;
case NOTE_INSN_REPEATED_LINE_NUMBER:
- case NOTE_INSN_UNLIKELY_EXECUTED_CODE:
+ case NOTE_INSN_SWITCH_TEXT_SECTIONS:
emit_note_copy (insn);
break;
extern bool can_copy_bbs_p (basic_block *, unsigned);
extern void copy_bbs (basic_block *, unsigned, basic_block *,
edge *, unsigned, edge *, struct loop *);
-extern bool scan_ahead_for_unlikely_executed_note (rtx);
extern rtx duplicate_insn_chain (rtx, rtx);
#endif /* GCC_CFGLAYOUT_H */
can_delete_note_p (rtx note)
{
return (NOTE_LINE_NUMBER (note) == NOTE_INSN_DELETED
- || NOTE_LINE_NUMBER (note) == NOTE_INSN_BASIC_BLOCK
- || NOTE_LINE_NUMBER (note) == NOTE_INSN_UNLIKELY_EXECUTED_CODE);
+ || NOTE_LINE_NUMBER (note) == NOTE_INSN_BASIC_BLOCK);
}
/* True if a given label can be deleted. */
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (find_reg_note (BB_END (a), REG_CROSSING_JUMP, NULL_RTX)
- || find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX)
- || BB_PARTITION (a) != BB_PARTITION (b)))
+ if (BB_PARTITION (a) != BB_PARTITION (b))
return false;
/* There must be exactly one edge in between the blocks. */
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (find_reg_note (insn, REG_CROSSING_JUMP, NULL_RTX)
- || BB_PARTITION (src) != BB_PARTITION (target)))
+ if (find_reg_note (insn, REG_CROSSING_JUMP, NULL_RTX)
+ || BB_PARTITION (src) != BB_PARTITION (target))
return NULL;
/* We can replace or remove a complex jump only when we have exactly
BB_COPY_PARTITION (jump_block, e->src);
if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections)
- {
- if (BB_PARTITION (jump_block) == BB_COLD_PARTITION)
- {
- rtx bb_note, new_note;
- for (bb_note = BB_HEAD (jump_block);
- bb_note && bb_note != NEXT_INSN (BB_END (jump_block));
- bb_note = NEXT_INSN (bb_note))
- if (NOTE_P (bb_note)
- && NOTE_LINE_NUMBER (bb_note) == NOTE_INSN_BASIC_BLOCK)
- break;
- new_note = emit_note_after (NOTE_INSN_UNLIKELY_EXECUTED_CODE,
- bb_note);
- NOTE_BASIC_BLOCK (new_note) = jump_block;
- }
- if (JUMP_P (BB_END (jump_block))
- && !any_condjump_p (BB_END (jump_block))
- && (single_succ_edge (jump_block)->flags & EDGE_CROSSING))
- REG_NOTES (BB_END (jump_block)) = gen_rtx_EXPR_LIST
- (REG_CROSSING_JUMP, NULL_RTX,
- REG_NOTES (BB_END (jump_block)));
- }
-
+ && targetm.have_named_sections
+ && JUMP_P (BB_END (jump_block))
+ && !any_condjump_p (BB_END (jump_block))
+ && (EDGE_SUCC (jump_block, 0)->flags & EDGE_CROSSING))
+ REG_NOTES (BB_END (jump_block)) = gen_rtx_EXPR_LIST (REG_CROSSING_JUMP,
+ NULL_RTX,
+ REG_NOTES
+ (BB_END
+ (jump_block)));
+
/* Wire edge in. */
new_edge = make_edge (e->src, jump_block, EDGE_FALLTHRU);
new_edge->probability = e->probability;
tmp = NEXT_INSN (tmp);
if (NOTE_INSN_BASIC_BLOCK_P (tmp))
tmp = NEXT_INSN (tmp);
- if (tmp
- && NOTE_P (tmp)
- && NOTE_LINE_NUMBER (tmp) == NOTE_INSN_UNLIKELY_EXECUTED_CODE)
- tmp = NEXT_INSN (tmp);
if (tmp == BB_HEAD (bb))
before = tmp;
else if (tmp)
&& BB_PARTITION (e->src) == BB_COLD_PARTITION
&& !(e->flags & EDGE_CROSSING))
{
- rtx bb_note, new_note, cur_insn;
+ rtx bb_note, cur_insn;
bb_note = NULL_RTX;
for (cur_insn = BB_HEAD (bb); cur_insn != NEXT_INSN (BB_END (bb));
break;
}
- new_note = emit_note_after (NOTE_INSN_UNLIKELY_EXECUTED_CODE,
- bb_note);
- NOTE_BASIC_BLOCK (new_note) = bb;
if (JUMP_P (BB_END (bb))
&& !any_condjump_p (BB_END (bb))
&& (single_succ_edge (bb)->flags & EDGE_CROSSING))
REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
(REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
- if (after == bb_note)
- after = new_note;
}
}
}
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && (find_reg_note (BB_END (a), REG_CROSSING_JUMP, NULL_RTX)
- || find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX)
- || BB_PARTITION (a) != BB_PARTITION (b)))
+ if (BB_PARTITION (a) != BB_PARTITION (b))
return false;
/* There must be exactly one edge in between the blocks. */
bool weak_p = DECL_P (exp) && DECL_WEAK (exp);
static void (* const base_funs[][2])(void) = {
{ text_section, text_coal_section },
- { text_unlikely_section, text_unlikely_coal_section },
+ { unlikely_text_section, text_unlikely_coal_section },
{ readonly_data_section, const_coal_section },
{ const_data_section, const_data_coal_section },
{ data_section, data_coal_section }
};
+ if (reloc == 0
+ && (last_text_section == in_text_unlikely
+ || last_text_section == in_text_unlikely_coal))
+ reloc = 1;
+
if (TREE_CODE (exp) == FUNCTION_DECL)
base_function = base_funs[reloc][weak_p];
else if (decl_readonly_section_1 (exp, reloc, MACHOPIC_INDIRECT))
if (asm_out_file) \
fputs ("\t" DIRECTIVE "\n", asm_out_file); \
in_section = SECTION; \
+ if ((SECTION == in_text_coal) \
+ || (SECTION == in_text_unlikely) \
+ || (SECTION == in_text_unlikely_coal)) \
+ last_text_section = SECTION; \
} \
} \
in_text_coal, \
".section __TEXT,__textcoal_nt,coalesced," \
"pure_instructions", 0) \
-SECTION_FUNCTION (text_unlikely_section, \
- in_text_unlikely, \
- ".section __TEXT,__text_unlikely,coalesced," \
- "pure_instructions", 0) \
SECTION_FUNCTION (text_unlikely_coal_section, \
in_text_unlikely_coal, \
".section __TEXT,__text_unlikely_coal," \
return;
/* Align to cache line in the function's code section. */
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (align > 0)
{
int vlen, idx;
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
vlen = XVECLEN (table, 0);
for (idx = 0; idx < vlen; idx++)
/* There's no need for -fPIC (as opposed to -fpic) on Xtensa. */
if (flag_pic > 1)
flag_pic = 1;
+
+ /* Hot/cold partitioning does not work on this architecture, because of
+ constant pools (the load instruction cannot necessarily reach that far).
+ Therefore disable it on this architecture. */
+ if (flag_reorder_blocks_and_partition)
+ {
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
}
static const char *base_input_file;
#ifdef DEBUG_SYMS_TEXT
-#define FORCE_TEXT function_section (current_function_decl);
+#define FORCE_TEXT current_function_section (current_function_decl);
#else
#define FORCE_TEXT
#endif
debug_nothing_rtx, /* label */
dbxout_handle_pch, /* handle_pch */
debug_nothing_rtx, /* var_location */
+ debug_nothing_void, /* switch_text_section */
0 /* start_end_main_source_file */
};
#endif /* DBX_DEBUGGING_INFO */
debug_nothing_rtx, /* label */
dbxout_handle_pch, /* handle_pch */
debug_nothing_rtx, /* var_location */
+ debug_nothing_void, /* switch_text_section */
0 /* start_end_main_source_file */
};
#endif /* XCOFF_DEBUGGING_INFO */
#ifdef DBX_OUTPUT_NFUN
DBX_OUTPUT_NFUN (asm_out_file, lscope_label_name, current_function_decl);
#else
- dbxout_begin_empty_stabs (N_FUN);
- dbxout_stab_value_label_diff (lscope_label_name,
- XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+ if (flag_reorder_blocks_and_partition)
+ {
+ dbxout_begin_empty_stabs (N_FUN);
+ dbxout_stab_value_label_diff (hot_section_end_label, hot_section_label);
+ dbxout_begin_empty_stabs (N_FUN);
+ dbxout_stab_value_label_diff (cold_section_end_label,
+ unlikely_section_label);
+ }
+ else
+ {
+ dbxout_begin_empty_stabs (N_FUN);
+ dbxout_stab_value_label_diff (lscope_label_name,
+ XSTR (XEXP (DECL_RTL (current_function_decl),
+ 0), 0));
+ }
#endif
debug_nothing_rtx, /* label */
debug_nothing_int, /* handle_pch */
debug_nothing_rtx, /* var_location */
+ debug_nothing_void, /* switch_text_section */
0 /* start_end_main_source_file */
};
/* Called from final_scan_insn for any NOTE_INSN_VAR_LOCATION note. */
void (* var_location) (rtx);
+ /* Called from final_scan_insn if there is a switch between hot and cold
+ text sections. */
+ void (* switch_text_section) (void);
+
/* This is 1 if the debug writer wants to see start and end commands for the
main source files, and 0 otherwise. */
int start_end_main_source_file;
const char *dw_fde_begin;
const char *dw_fde_current_label;
const char *dw_fde_end;
+ const char *dw_fde_hot_section_label;
+ const char *dw_fde_hot_section_end_label;
+ const char *dw_fde_unlikely_section_label;
+ const char *dw_fde_unlikely_section_end_label;
+ bool dw_fde_switched_sections;
dw_cfi_ref dw_fde_cfi;
unsigned funcdef_number;
unsigned all_throwers_are_sibcalls : 1;
dw2_asm_output_encoded_addr_rtx (fde_encoding,
sym_ref,
"FDE initial location");
- dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
- fde->dw_fde_end, fde->dw_fde_begin,
- "FDE address range");
+ if (fde->dw_fde_switched_sections)
+ {
+ rtx sym_ref2 = gen_rtx_SYMBOL_REF (Pmode,
+ fde->dw_fde_unlikely_section_label);
+ rtx sym_ref3= gen_rtx_SYMBOL_REF (Pmode,
+ fde->dw_fde_hot_section_label);
+ SYMBOL_REF_FLAGS (sym_ref2) |= SYMBOL_FLAG_LOCAL;
+ SYMBOL_REF_FLAGS (sym_ref3) |= SYMBOL_FLAG_LOCAL;
+ dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref3,
+ "FDE initial location");
+ dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+ fde->dw_fde_hot_section_end_label,
+ fde->dw_fde_hot_section_label,
+ "FDE address range");
+ dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref2,
+ "FDE initial location");
+ dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+ fde->dw_fde_unlikely_section_end_label,
+ fde->dw_fde_unlikely_section_label,
+ "FDE address range");
+ }
+ else
+ dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+ fde->dw_fde_end, fde->dw_fde_begin,
+ "FDE address range");
}
else
{
dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin,
"FDE initial location");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- fde->dw_fde_end, fde->dw_fde_begin,
- "FDE address range");
+ if (fde->dw_fde_switched_sections)
+ {
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+ fde->dw_fde_hot_section_label,
+ "FDE initial location");
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+ fde->dw_fde_hot_section_end_label,
+ fde->dw_fde_hot_section_label,
+ "FDE address range");
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+ fde->dw_fde_unlikely_section_label,
+ "FDE initial location");
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+ fde->dw_fde_unlikely_section_end_label,
+ fde->dw_fde_unlikely_section_label,
+ "FDE address range");
+ }
+ else
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+ fde->dw_fde_end, fde->dw_fde_begin,
+ "FDE address range");
}
if (augmentation[0])
fde->decl = current_function_decl;
fde->dw_fde_begin = dup_label;
fde->dw_fde_current_label = NULL;
+ fde->dw_fde_hot_section_label = NULL;
+ fde->dw_fde_hot_section_end_label = NULL;
+ fde->dw_fde_unlikely_section_label = NULL;
+ fde->dw_fde_unlikely_section_end_label = NULL;
+ fde->dw_fde_switched_sections = false;
fde->dw_fde_end = NULL;
fde->dw_fde_cfi = NULL;
fde->funcdef_number = current_function_funcdef_no;
static void dwarf2out_abstract_function (tree);
static void dwarf2out_var_location (rtx);
static void dwarf2out_begin_function (tree);
+static void dwarf2out_switch_text_section (void);
/* The debug hooks structure. */
debug_nothing_rtx, /* label */
debug_nothing_int, /* handle_pch */
dwarf2out_var_location,
+ dwarf2out_switch_text_section,
1 /* start_end_main_source_file */
};
#endif
{
rtx GTY (()) var_loc_note;
const char * GTY (()) label;
+ const char * GTY (()) section_label;
struct var_loc_node * GTY (()) next;
};
*d = new_loc_list (descr, begin, end, section, 0);
}
+static void
+dwarf2out_switch_text_section (void)
+{
+ dw_fde_ref fde;
+
+ fde = &fde_table[fde_table_in_use - 1];
+ fde->dw_fde_switched_sections = true;
+ fde->dw_fde_hot_section_label = xstrdup (hot_section_label);
+ fde->dw_fde_hot_section_end_label = xstrdup (hot_section_end_label);
+ fde->dw_fde_unlikely_section_label = xstrdup (unlikely_section_label);
+ fde->dw_fde_unlikely_section_end_label = xstrdup (cold_section_end_label);
+ separate_line_info_table_in_use++;
+}
+
/* Output the location list given to us. */
static void
}
dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_section_label, "Address");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
- text_section_label, "Length");
+ if (last_text_section == in_unlikely_executed_text
+ || (last_text_section == in_named
+ && last_text_section_name == unlikely_text_section_name))
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
+ unlikely_section_label, "Length");
+ else
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
+ text_section_label, "Length");
for (i = 0; i < arange_table_in_use; i++)
{
base of the text section. */
if (separate_line_info_table_in_use == 0)
{
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
- text_section_label,
- fmt, i * 2 * DWARF2_ADDR_SIZE);
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
- text_section_label, NULL);
+ if (last_text_section == in_unlikely_executed_text
+ || (last_text_section == in_named
+ && last_text_section_name == unlikely_text_section_name))
+ {
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
+ unlikely_section_label,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
+ unlikely_section_label, NULL);
+ }
+ else
+ {
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
+ text_section_label,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
+ text_section_label, NULL);
+ }
}
/* Otherwise, we add a DW_AT_entry_pc attribute to force the
a series of state machine operations. */
current_file = 1;
current_line = 1;
- strcpy (prev_line_label, text_section_label);
+ if (last_text_section == in_unlikely_executed_text
+ || (last_text_section == in_named
+ && last_text_section_name == unlikely_text_section_name))
+ strcpy (prev_line_label, unlikely_section_label);
+ else
+ strcpy (prev_line_label, text_section_label);
for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index)
{
dw_line_info_ref line_info = &line_info_table[lt_index];
tree sectree = DECL_SECTION_NAME (current_function_decl);
secname = TREE_STRING_POINTER (sectree);
}
+ else if (last_text_section == in_unlikely_executed_text
+ || (last_text_section == in_named
+ && last_text_section_name == unlikely_text_section_name))
+ secname = unlikely_section_label;
else
secname = text_section_label;
if (!old_die || !get_AT (old_die, DW_AT_inline))
equate_decl_number_to_die (decl, subr_die);
- ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL,
- current_function_funcdef_no);
- add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id);
- ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
- current_function_funcdef_no);
- add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id);
-
- add_pubname (decl, subr_die);
- add_arange (decl, subr_die);
+ if (!flag_reorder_blocks_and_partition)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL,
+ current_function_funcdef_no);
+ add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id);
+ ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
+ current_function_funcdef_no);
+ add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id);
+
+ add_pubname (decl, subr_die);
+ add_arange (decl, subr_die);
+ }
+ else
+ { /* Do nothing for now; maybe need to duplicate die, one for
+ hot section and ond for cold section, then use the hot/cold
+ section begin/end labels to generate the aranges... */
+ /*
+ add_AT_lbl_id (subr_die, DW_AT_low_pc, hot_section_label);
+ add_AT_lbl_id (subr_die, DW_AT_high_pc, hot_section_end_label);
+ add_AT_lbl_id (subr_die, DW_AT_lo_user, unlikely_section_label);
+ add_AT_lbl_id (subr_die, DW_AT_hi_user, cold_section_end_label);
+
+ add_pubname (decl, subr_die);
+ add_arange (decl, subr_die);
+ add_arange (decl, subr_die);
+ */
+ }
#ifdef MIPS_DEBUGGING_INFO
/* Add a reference to the FDE for this routine. */
dwarf2out_begin_block (unsigned int line ATTRIBUTE_UNUSED,
unsigned int blocknum)
{
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum);
}
static void
dwarf2out_end_block (unsigned int line ATTRIBUTE_UNUSED, unsigned int blocknum)
{
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_END_LABEL, blocknum);
}
newloc->var_loc_note = loc_note;
newloc->next = NULL;
+ if (last_text_section == in_unlikely_executed_text
+ || (last_text_section == in_named
+ && last_text_section_name == unlikely_text_section_name))
+ newloc->section_label = unlikely_section_label;
+ else
+ newloc->section_label = text_section_label;
+
last_insn = loc_note;
last_label = newloc->label;
decl = NOTE_VAR_LOCATION_DECL (loc_note);
if (debug_info_level >= DINFO_LEVEL_NORMAL
&& line != 0)
{
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
/* If requested, emit something human-readable. */
if (flag_debug_asm)
dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->ehspec_data, i),
(i ? NULL : "Exception specification table"));
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
}
#include "gt-except.h"
assemble_integer (const0_rtx, LONG_TYPE_SIZE / BITS_PER_UNIT, align, 1);
}
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
#if defined(ASM_OUTPUT_REG_PUSH)
if (sval && svrtx != NULL_RTX && REG_P (svrtx))
}
}
-/* Return boolean indicating if there is a NOTE_INSN_UNLIKELY_EXECUTED_CODE
- note in the instruction chain (going forward) between the current
- instruction, and the next 'executable' instruction. */
-
-bool
-scan_ahead_for_unlikely_executed_note (rtx insn)
-{
- rtx temp;
- int bb_note_count = 0;
-
- for (temp = insn; temp; temp = NEXT_INSN (temp))
- {
- if (NOTE_P (temp)
- && NOTE_LINE_NUMBER (temp) == NOTE_INSN_UNLIKELY_EXECUTED_CODE)
- return true;
- if (NOTE_P (temp)
- && NOTE_LINE_NUMBER (temp) == NOTE_INSN_BASIC_BLOCK)
- {
- bb_note_count++;
- if (bb_note_count > 1)
- return false;
- }
- if (INSN_P (temp))
- return false;
- }
-
- return false;
-}
-
/* The final scan for one insn, INSN.
Args are same as in `final', except that INSN
is the insn being scanned.
case NOTE_INSN_EXPECTED_VALUE:
break;
- case NOTE_INSN_UNLIKELY_EXECUTED_CODE:
+ case NOTE_INSN_SWITCH_TEXT_SECTIONS:
/* The presence of this note indicates that this basic block
belongs in the "cold" section of the .o file. If we are
not already writing to the cold section we need to change
to it. */
-
- unlikely_text_section ();
+
+ if (last_text_section == in_text)
+ {
+ (*debug_hooks->switch_text_section) ();
+ unlikely_text_section ();
+ }
+ else
+ {
+ (*debug_hooks->switch_text_section) ();
+ text_section ();
+ }
break;
case NOTE_INSN_BASIC_BLOCK:
- /* If we are performing the optimization that partitions
- basic blocks into hot & cold sections of the .o file,
- then at the start of each new basic block, before
- beginning to write code for the basic block, we need to
- check to see whether the basic block belongs in the hot
- or cold section of the .o file, and change the section we
- are writing to appropriately. */
-
- if (flag_reorder_blocks_and_partition
- && !scan_ahead_for_unlikely_executed_note (insn))
- function_section (current_function_decl);
-
#ifdef TARGET_UNWIND_INFO
targetm.asm_out.unwind_emit (asm_out_file, insn);
#endif
if (LABEL_NAME (insn))
(*debug_hooks->label) (insn);
- /* If we are doing the optimization that partitions hot & cold
- basic blocks into separate sections of the .o file, we need
- to ensure the jump table ends up in the correct section... */
-
- if (flag_reorder_blocks_and_partition
- && targetm.have_named_sections)
- {
- rtx tmp_table, tmp_label;
- if (LABEL_P (insn)
- && tablejump_p (NEXT_INSN (insn), &tmp_label, &tmp_table))
- {
- /* Do nothing; Do NOT change the current section. */
- }
- else if (scan_ahead_for_unlikely_executed_note (insn))
- unlikely_text_section ();
- else if (in_unlikely_text_section ())
- function_section (current_function_decl);
- }
-
if (app_on)
{
fputs (ASM_APP_OFF, file);
ASM_OUTPUT_ALIGN (file, log_align);
}
else
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn),
if (! JUMP_TABLES_IN_TEXT_SECTION)
targetm.asm_out.function_rodata_section (current_function_decl);
else
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
if (app_on)
{
#endif
#endif
- function_section (current_function_decl);
+ current_function_section (current_function_decl);
break;
}
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && ((BB_END (then_bb)
- && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
- || (BB_END (else_bb)
- && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
- NULL_RTX))))
+ if ((BB_END (then_bb)
+ && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
+ || (BB_END (test_bb)
+ && find_reg_note (BB_END (test_bb), REG_CROSSING_JUMP, NULL_RTX))
+ || (BB_END (else_bb)
+ && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
+ NULL_RTX)))
return FALSE;
/* THEN has one successor. */
partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && ((BB_END (then_bb)
- && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
- || (BB_END (else_bb)
- && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
- NULL_RTX))))
+ if ((BB_END (then_bb)
+ && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
+ || (BB_END (test_bb)
+ && find_reg_note (BB_END (test_bb), REG_CROSSING_JUMP, NULL_RTX))
+ || (BB_END (else_bb)
+ && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
+ NULL_RTX)))
return FALSE;
/* ELSE has one successor. */
now included in every insn. */
INSN_NOTE (BASIC_BLOCK)
-/* Record that the current basic block is unlikely to be executed and
- should be moved to the UNLIKELY_EXECUTED_TEXT_SECTION. FIXME: Make
- this a bit on the basic block structure. */
-INSN_NOTE (UNLIKELY_EXECUTED_CODE)
+/* Mark the inflection point in the instruction stream where we switch
+ between hot and cold text sections. */
+INSN_NOTE (SWITCH_TEXT_SECTIONS)
#undef INSN_NOTE
if (flag_exceptions && flag_reorder_blocks_and_partition)
{
- warning
+ inform
("-freorder-blocks-and-partition does not work with exceptions");
flag_reorder_blocks_and_partition = 0;
flag_reorder_blocks = 1;
}
-
- /* The optimization to partition hot and cold basic blocks into
- separate sections of the .o and executable files does not currently
- work correctly with DWARF debugging turned on. Until this is fixed
- we will disable the optimization when DWARF debugging is set. */
-
- if (flag_reorder_blocks_and_partition && write_symbols == DWARF2_DEBUG)
- {
- warning
- ("-freorder-blocks-and-partition does not work with -g (currently)");
- flag_reorder_blocks_and_partition = 0;
- flag_reorder_blocks = 1;
- }
}
/* Handle target- and language-independent options. Return zero to
/* Tell assembler to switch to the section for function DECL. */
extern void function_section (tree);
+/* Tell assembler to switch to the most recently used text section. */
+extern void current_function_section (tree);
+
/* Tell assembler to switch to the section for string merging. */
extern void mergeable_string_section (tree, unsigned HOST_WIDE_INT,
unsigned int);
extern int size_directive_output;
extern tree last_assemble_variable_decl;
+enum in_section { no_section, in_text, in_unlikely_executed_text, in_data,
+ in_named
+#ifdef BSS_SECTION_ASM_OP
+ , in_bss
+#endif
+#ifdef CTORS_SECTION_ASM_OP
+ , in_ctors
+#endif
+#ifdef DTORS_SECTION_ASM_OP
+ , in_dtors
+#endif
+#ifdef READONLY_DATA_SECTION_ASM_OP
+ , in_readonly_data
+#endif
+#ifdef EXTRA_SECTIONS
+ , EXTRA_SECTIONS
+#endif
+};
+
+extern char *unlikely_section_label;
+extern char *hot_section_label;
+extern char *hot_section_end_label;
+extern char *cold_section_end_label;
+extern char *unlikely_text_section_name;
+extern const char *last_text_section_name;
+extern enum in_section last_text_section;
+extern bool first_function_block_is_cold;
+
/* Decide whether DECL needs to be in a writable section.
RELOC is the same as for SELECT_SECTION. */
extern bool decl_readonly_section (tree, int);
extern int default_address_cost (rtx);
+/* When performing hot/cold basic block partitioning, insert note in
+ instruction stream indicating boundary between hot and cold sections. */
+extern void insert_section_boundary_note (void);
+
/* dbxout helper functions */
#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO
timevar_push (TV_SYMOUT);
(*debug_hooks->function_decl) (current_function_decl);
+ if (unlikely_text_section_name)
+ free (unlikely_text_section_name);
timevar_pop (TV_SYMOUT);
ggc_collect ();
}
break;
- case NOTE_INSN_UNLIKELY_EXECUTED_CODE:
+ case NOTE_INSN_SWITCH_TEXT_SECTIONS:
{
#ifndef GENERATOR_FILE
basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
if (LABEL_P (tmp)
|| CALL_P (tmp)
|| NOTE_INSN_BASIC_BLOCK_P (tmp)
- || (NOTE_P (tmp)
- && NOTE_LINE_NUMBER (tmp) == NOTE_INSN_UNLIKELY_EXECUTED_CODE)
|| (NONJUMP_INSN_P (tmp)
&& stack_regs_mentioned (tmp)))
{
sdbout_label, /* label */
debug_nothing_int, /* handle_pch */
debug_nothing_rtx, /* var_location */
+ debug_nothing_void, /* switch_text_section */
0 /* start_end_main_source_file */
};
#include "tree-mudflap.h"
#include "cgraph.h"
#include "cfglayout.h"
+#include "basic-block.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
tree last_assemble_variable_decl;
-/* The following global variable indicates if the section label for the
- "cold" section of code has been output yet to the assembler. The
- label is useful when running gdb. This is part of the optimization that
- partitions hot and cold basic blocks into separate sections of the .o
- file. */
+/* The following global variable indicates if the first basic block
+ in a function belongs to the cold partition or not. */
-static bool unlikely_section_label_printed = false;
+bool first_function_block_is_cold;
/* The following global variable indicates the label name to be put at
the start of the first cold section within each function, when
- partitioning basic blocks into hot and cold sections. */
+ partitioning basic blocks into hot and cold sections. Used for
+ debug info. */
-static char *unlikely_section_label = NULL;
+char *unlikely_section_label;
+
+/* The following global variable indicates the label name to be put at
+ the start of the first hot section within each function, when
+ partitioning basic blocks into hot and cold sections. Used for
+ debug info. */
+
+char *hot_section_label;
+
+/* The following global variable indicates the label name to be put at
+ the end of the last hot section within each function, when
+ partitioning basic blocks into hot and cold sections. Used for
+ debug info. */
+
+char *hot_section_end_label;
+
+/* The following global variable indicates the label name to be put at
+ the end of the last cold section within each function, when
+ partitioning basic blocks into hot and cold sections. Used for
+ debug info.*/
+
+char *cold_section_end_label;
-/* The following global variable indicates the section name to be used
- for the current cold section, when partitioning hot and cold basic
+/* The following global variable indicates the seciton name to be used
+ for the current cold section, when partitiong hot and cold basic
blocks into separate sections. */
-static char *unlikely_text_section_name = NULL;
+char *unlikely_text_section_name;
/* We give all constants their own alias set. Perhaps redundant with
MEM_READONLY_P, but pre-dates it. */
static void maybe_assemble_visibility (tree);
static int in_named_entry_eq (const void *, const void *);
static hashval_t in_named_entry_hash (const void *);
+static void initialize_cold_section_name (void);
#ifdef BSS_SECTION_ASM_OP
#ifdef ASM_OUTPUT_BSS
static void asm_output_bss (FILE *, tree, const char *,
unsigned HOST_WIDE_INT);
static void mark_weak (tree);
\f
-enum in_section { no_section, in_text, in_unlikely_executed_text, in_data,
- in_named
-#ifdef BSS_SECTION_ASM_OP
- , in_bss
-#endif
-#ifdef CTORS_SECTION_ASM_OP
- , in_ctors
-#endif
-#ifdef DTORS_SECTION_ASM_OP
- , in_dtors
-#endif
-#ifdef READONLY_DATA_SECTION_ASM_OP
- , in_readonly_data
-#endif
-#ifdef EXTRA_SECTIONS
- , EXTRA_SECTIONS
-#endif
-};
static GTY(()) enum in_section in_section = no_section;
+enum in_section last_text_section;
/* Return a nonzero value if DECL has a section attribute. */
#ifndef IN_NAMED_SECTION
/* Text of section name when in_section == in_named. */
static GTY(()) const char *in_named_name;
+const char *last_text_section_name;
/* Hash table of flags that have been used for a particular named section. */
EXTRA_SECTION_FUNCTIONS
#endif
-/* Tell assembler to switch to text section. */
-
-void
-text_section (void)
-{
- if (in_section != in_text)
- {
- in_section = in_text;
- fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
- }
-}
-
-/* Tell assembler to switch to unlikely-to-be-executed text section. */
-
-void
-unlikely_text_section (void)
+static void
+initialize_cold_section_name (void)
{
- const char *name;
+ const char* name;
int len;
if (! unlikely_text_section_name)
name = TREE_STRING_POINTER (DECL_SECTION_NAME
(current_function_decl));
len = strlen (name);
- unlikely_text_section_name = xmalloc ((len + 10) * sizeof (char));
- strcpy (unlikely_text_section_name, name);
- strcat (unlikely_text_section_name, "_unlikely");
+ unlikely_text_section_name = xmalloc (len + 10);
+ sprintf (unlikely_text_section_name, "%s%s", name, "_unlikely");
}
else
- {
- len = strlen (UNLIKELY_EXECUTED_TEXT_SECTION_NAME);
- unlikely_text_section_name = xmalloc (len+1 * sizeof (char));
- strcpy (unlikely_text_section_name,
- UNLIKELY_EXECUTED_TEXT_SECTION_NAME);
- }
+ unlikely_text_section_name =
+ xstrdup (UNLIKELY_EXECUTED_TEXT_SECTION_NAME);
+ }
+}
+
+/* Tell assembler to switch to text section. */
+
+void
+text_section (void)
+{
+ if (in_section != in_text)
+ {
+ in_section = in_text;
+ last_text_section = in_text;
+ fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
}
+}
+
+/* Tell assembler to switch to unlikely-to-be-executed text section. */
+
+void
+unlikely_text_section (void)
+{
+ if (! unlikely_text_section_name)
+ initialize_cold_section_name ();
if ((in_section != in_unlikely_executed_text)
&& (in_section != in_named
{
named_section (NULL_TREE, unlikely_text_section_name, 0);
in_section = in_unlikely_executed_text;
-
- if (!unlikely_section_label_printed)
- {
- ASM_OUTPUT_LABEL (asm_out_file, unlikely_section_label);
- unlikely_section_label_printed = true;
- }
+ last_text_section = in_unlikely_executed_text;
}
}
in_section = in_named;
}
}
+
+ if (in_text_section () || in_unlikely_text_section ())
+ {
+ last_text_section = in_section;
+ last_text_section_name = name;
+ }
}
/* Tell assembler to change to section NAME for DECL.
void
function_section (tree decl)
{
- if (decl == NULL_TREE)
- text_section ();
+ bool unlikely = false;
+
+ if (first_function_block_is_cold)
+ unlikely = true;
+
+#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
+ targetm.asm_out.select_section (decl, unlikely, DECL_ALIGN (decl));
+#else
+ if (decl != NULL_TREE
+ && DECL_SECTION_NAME (decl) != NULL_TREE)
+ named_section (decl, (char *) 0, 0);
else
- {
- /* ??? Typical use of this function maybe shouldn't be looking
- for unlikely blocks at all - in the event that an entire
- function is going into the unlikely-execute section, that
- should be reflected in its DECL_SECTION_NAME. */
- rtx insns = cfun && cfun->emit ? get_insns () : 0;
- bool unlikely = insns && scan_ahead_for_unlikely_executed_note (insns);
+ text_section ();
+#endif
+}
+void
+current_function_section (tree decl)
+{
#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
- targetm.asm_out.select_section (decl, unlikely, DECL_ALIGN (decl));
+ bool unlikely = (in_unlikely_text_section ()
+ || (last_text_section == in_unlikely_executed_text));
+
+ targetm.asm_out.select_section (decl, unlikely, DECL_ALIGN (decl));
#else
- if (unlikely)
- unlikely_text_section ();
- else if (DECL_SECTION_NAME (decl))
- named_section (decl, 0, 0);
- else
- text_section ();
+ if (last_text_section == in_unlikely_executed_text)
+ unlikely_text_section ();
+ else if (last_text_section == in_text)
+ text_section ();
+ else if (last_text_section == in_named)
+ named_section (NULL_TREE, last_text_section_name, 0);
+ else
+ function_section (decl);
#endif
- }
}
/* Switch to read-only data section associated with function DECL. */
assemble_start_function (tree decl, const char *fnname)
{
int align;
+ bool hot_label_written = false;
- if (unlikely_text_section_name)
- free (unlikely_text_section_name);
-
- unlikely_section_label_printed = false;
unlikely_text_section_name = NULL;
+ first_function_block_is_cold = false;
+ hot_section_label = reconcat (hot_section_label, fnname, ".hot_section", NULL);
unlikely_section_label = reconcat (unlikely_section_label,
fnname, ".unlikely_section", NULL);
-
+ hot_section_end_label = reconcat (hot_section_end_label,
+ fnname, ".end", NULL);
+ cold_section_end_label = reconcat (cold_section_end_label,
+ fnname, ".end.cold", NULL);
+
/* The following code does not need preprocessing in the assembler. */
app_disable ();
if (CONSTANT_POOL_BEFORE_FUNCTION)
output_constant_pool (fnname, decl);
- /* Make sure the cold text (code) section is properly aligned. This
- is necessary here in the case where the function has both hot and
- cold sections, because we don't want to re-set the alignment when the
- section switch happens mid-function. We don't need to set the hot
- section alignment here, because code further down in this function
- sets the alignment for whichever section comes first, and if there
- is a hot section it is guaranteed to be first. */
+ /* Make sure the not and cold text (code) sections are properly
+ aligned. This is necessary here in the case where the function
+ has both hot and cold sections, because we don't want to re-set
+ the alignment when the section switch happens mid-function. */
if (flag_reorder_blocks_and_partition)
{
unlikely_text_section ();
assemble_align (FUNCTION_BOUNDARY);
+ ASM_OUTPUT_LABEL (asm_out_file, unlikely_section_label);
+ if (BB_PARTITION (ENTRY_BLOCK_PTR->next_bb) == BB_COLD_PARTITION)
+ {
+ /* Since the function starts with a cold section, we need to
+ explicitly align the hot section and write out the hot
+ section label. */
+ text_section ();
+ assemble_align (FUNCTION_BOUNDARY);
+ ASM_OUTPUT_LABEL (asm_out_file, hot_section_label);
+ hot_label_written = true;
+ first_function_block_is_cold = true;
+ }
}
+ else if (DECL_SECTION_NAME (decl))
+ {
+ /* Calls to function_section rely on first_function_block_is_cold
+ being accurate. The first block may be cold even if we aren't
+ doing partitioning, if the entire function was decided by
+ choose_function_section (predict.c) to be cold. */
+
+ int i;
+ int len;
+ char *s;
+ initialize_cold_section_name ();
+
+ /* The following is necessary, because 'strcmp
+ (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)), blah)' always
+ fails, presumably because TREE_STRING_POINTER is declared to
+ be an array of size 1 of char. */
+
+ len = TREE_STRING_LENGTH (DECL_SECTION_NAME (decl));
+ s = (char *) xmalloc (len + 1);
+
+ for (i = 0; i < len; i ++)
+ s[i] = (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)))[i];
+ s[len] = '\0';
+
+ if (unlikely_text_section_name
+ && (strcmp (s, unlikely_text_section_name) == 0))
+ first_function_block_is_cold = true;
+ }
+
+ last_text_section = no_section;
+ in_section = no_section;
resolve_unique_section (decl, 0, flag_function_sections);
+
+ /* Switch to the correct text section for the start of the function. */
+
function_section (decl);
+ if (!hot_label_written)
+ ASM_OUTPUT_LABEL (asm_out_file, hot_section_label);
/* Tell assembler to move to target machine's alignment for functions. */
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
ASM_OUTPUT_LABEL (asm_out_file, fnname);
#endif /* ASM_DECLARE_FUNCTION_NAME */
- if (in_unlikely_text_section ()
- && !unlikely_section_label_printed)
- {
- ASM_OUTPUT_LABEL (asm_out_file, unlikely_section_label);
- unlikely_section_label_printed = true;
- }
+ insert_section_boundary_note ();
}
/* Output assembler code associated with defining the size of the
void
assemble_end_function (tree decl, const char *fnname)
{
+ enum in_section save_text_section;
#ifdef ASM_DECLARE_FUNCTION_SIZE
ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl);
#endif
output_constant_pool (fnname, decl);
function_section (decl); /* need to switch back */
}
+ /* Output labels for end of hot/cold text sections (to be used by
+ debug info.) */
+ save_text_section = in_section;
+ unlikely_text_section ();
+ ASM_OUTPUT_LABEL (asm_out_file, cold_section_end_label);
+ text_section ();
+ ASM_OUTPUT_LABEL (asm_out_file, hot_section_end_label);
+ if (save_text_section == in_unlikely_executed_text)
+ unlikely_text_section ();
}
\f
/* Assemble code to leave SIZE bytes of zeros. */
debug_nothing_rtx, /* label */
debug_nothing_int, /* handle_pch */
debug_nothing_rtx, /* var_location */
+ debug_nothing_void, /* switch_text_section */
0 /* start_end_main_source_file */
};