#include "config.h"
#include "system.h"
#include "toplev.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "regs.h"
#include "function.h"
#include "flags.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "except.h"
#include "toplev.h"
#include "recog.h"
#include "sched-int.h"
#include "target.h"
#ifdef INSN_SCHEDULING
static int issue_rate;
int insert_schedule_bubbles_p = 0;
static int sched_verbose_param = 0;
int sched_verbose = 0;
FILE *sched_dump = 0;
static int old_max_uid;
void
fix_sched_param (param, val)
const char *param, *val;
{
if (!strcmp (param, "verbose"))
sched_verbose_param = atoi (val);
else
warning ("fix_sched_param: unknown param: %s", param);
}
struct haifa_insn_data *h_i_d;
#define LINE_NOTE(INSN) (h_i_d[INSN_UID (INSN)].line_note)
#define INSN_TICK(INSN) (h_i_d[INSN_UID (INSN)].tick)
static rtx *line_note_head;
static rtx note_list;
#define MAX_INSN_QUEUE_INDEX max_insn_queue_index_macro_value
static rtx *insn_queue;
static int q_ptr = 0;
static int q_size = 0;
#define NEXT_Q(X) (((X)+1) & MAX_INSN_QUEUE_INDEX)
#define NEXT_Q_AFTER(X, C) (((X)+C) & MAX_INSN_QUEUE_INDEX)
static int max_insn_queue_index_macro_value;
state_t curr_state;
static size_t dfa_state_size;
static char *ready_try;
struct ready_list
{
rtx *vec;
int veclen;
int first;
int n_ready;
};
static unsigned int blockage_range PARAMS ((int, rtx));
static void clear_units PARAMS ((void));
static void schedule_unit PARAMS ((int, rtx, int));
static int actual_hazard PARAMS ((int, rtx, int, int));
static int potential_hazard PARAMS ((int, rtx, int));
static int priority PARAMS ((rtx));
static int rank_for_schedule PARAMS ((const PTR, const PTR));
static void swap_sort PARAMS ((rtx *, int));
static void queue_insn PARAMS ((rtx, int));
static int schedule_insn PARAMS ((rtx, struct ready_list *, int));
static int find_set_reg_weight PARAMS ((rtx));
static void find_insn_reg_weight PARAMS ((int));
static void adjust_priority PARAMS ((rtx));
static void advance_one_cycle PARAMS ((void));
static rtx unlink_other_notes PARAMS ((rtx, rtx));
static rtx unlink_line_notes PARAMS ((rtx, rtx));
static rtx reemit_notes PARAMS ((rtx, rtx));
static rtx *ready_lastpos PARAMS ((struct ready_list *));
static void ready_sort PARAMS ((struct ready_list *));
static rtx ready_remove_first PARAMS ((struct ready_list *));
static void queue_to_ready PARAMS ((struct ready_list *));
static void debug_ready_list PARAMS ((struct ready_list *));
static rtx move_insn1 PARAMS ((rtx, rtx));
static rtx move_insn PARAMS ((rtx, rtx));
static rtx ready_element PARAMS ((struct ready_list *, int));
static rtx ready_remove PARAMS ((struct ready_list *, int));
static int max_issue PARAMS ((struct ready_list *, int *));
static rtx choose_ready PARAMS ((struct ready_list *));
#endif
struct sched_info *current_sched_info;
#ifndef INSN_SCHEDULING
void
schedule_insns (dump_file)
FILE *dump_file ATTRIBUTE_UNUSED;
{
}
#else
static rtx last_scheduled_insn;
HAIFA_INLINE int
insn_unit (insn)
rtx insn;
{
int unit = INSN_UNIT (insn);
if (unit == 0)
{
recog_memoized (insn);
if (INSN_CODE (insn) < 0)
unit = -1;
else
{
unit = function_units_used (insn);
if (unit >= 0)
unit++;
}
if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT
|| unit >= 0
|| (unit & ~((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
INSN_UNIT (insn) = unit;
}
return (unit > 0 ? unit - 1 : unit);
}
HAIFA_INLINE static unsigned int
blockage_range (unit, insn)
int unit;
rtx insn;
{
unsigned int blockage = INSN_BLOCKAGE (insn);
unsigned int range;
if ((int) UNIT_BLOCKED (blockage) != unit + 1)
{
range = function_units[unit].blockage_range_function (insn);
if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS)
INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range);
}
else
range = BLOCKAGE_RANGE (blockage);
return range;
}
#if FUNCTION_UNITS_SIZE
static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
#else
static rtx unit_last_insn[1];
#endif
#if FUNCTION_UNITS_SIZE
static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
#else
static int unit_tick[1];
#endif
#if FUNCTION_UNITS_SIZE
static int unit_n_insns[FUNCTION_UNITS_SIZE];
#else
static int unit_n_insns[1];
#endif
rtx
get_unit_last_insn (instance)
int instance;
{
return unit_last_insn[instance];
}
static void
clear_units ()
{
memset ((char *) unit_last_insn, 0, sizeof (unit_last_insn));
memset ((char *) unit_tick, 0, sizeof (unit_tick));
memset ((char *) unit_n_insns, 0, sizeof (unit_n_insns));
}
HAIFA_INLINE int
insn_issue_delay (insn)
rtx insn;
{
int i, delay = 0;
int unit = insn_unit (insn);
if (unit >= 0)
{
if (function_units[unit].blockage_range_function &&
function_units[unit].blockage_function)
delay = function_units[unit].blockage_function (insn, insn);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0 && function_units[i].blockage_range_function
&& function_units[i].blockage_function)
delay = MAX (delay, function_units[i].blockage_function (insn, insn));
return delay;
}
HAIFA_INLINE int
actual_hazard_this_instance (unit, instance, insn, clock, cost)
int unit, instance, clock, cost;
rtx insn;
{
int tick = unit_tick[instance];
if (tick - clock > cost)
{
if (function_units[unit].blockage_range_function)
{
if (function_units[unit].blockage_function)
tick += (function_units[unit].blockage_function
(unit_last_insn[instance], insn)
- function_units[unit].max_blockage);
else
tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn))
- function_units[unit].max_blockage);
}
if (tick - clock > cost)
cost = tick - clock;
}
return cost;
}
HAIFA_INLINE static void
schedule_unit (unit, insn, clock)
int unit, clock;
rtx insn;
{
int i;
if (unit >= 0)
{
int instance = unit;
#if MAX_MULTIPLICITY > 1
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
if (!actual_hazard_this_instance (unit, instance, insn, clock, 0))
break;
instance += FUNCTION_UNITS_SIZE;
}
#endif
unit_last_insn[instance] = insn;
unit_tick[instance] = (clock + function_units[unit].max_blockage);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
schedule_unit (i, insn, clock);
}
HAIFA_INLINE static int
actual_hazard (unit, insn, clock, cost)
int unit, clock, cost;
rtx insn;
{
int i;
if (unit >= 0)
{
int instance = unit;
int best_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
#if MAX_MULTIPLICITY > 1
int this_cost;
if (best_cost > cost)
{
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
instance += FUNCTION_UNITS_SIZE;
this_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
if (this_cost < best_cost)
{
best_cost = this_cost;
if (this_cost <= cost)
break;
}
}
}
#endif
cost = MAX (cost, best_cost);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = actual_hazard (i, insn, clock, cost);
return cost;
}
HAIFA_INLINE static int
potential_hazard (unit, insn, cost)
int unit, cost;
rtx insn;
{
int i, ncost;
unsigned int minb, maxb;
if (unit >= 0)
{
minb = maxb = function_units[unit].max_blockage;
if (maxb > 1)
{
if (function_units[unit].blockage_range_function)
{
maxb = minb = blockage_range (unit, insn);
maxb = MAX_BLOCKAGE_COST (maxb);
minb = MIN_BLOCKAGE_COST (minb);
}
if (maxb > 1)
{
ncost = minb * 0x40 + maxb;
ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit;
if (ncost > cost)
cost = ncost;
}
}
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = potential_hazard (i, insn, cost);
return cost;
}
HAIFA_INLINE int
insn_cost (insn, link, used)
rtx insn, link, used;
{
int cost = INSN_COST (insn);
if (cost < 0)
{
if (recog_memoized (insn) < 0)
{
INSN_COST (insn) = 0;
return 0;
}
else
{
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
cost = insn_default_latency (insn);
else
cost = result_ready_cost (insn);
if (cost < 0)
cost = 0;
INSN_COST (insn) = cost;
}
}
if (link == 0 || used == 0)
return cost;
if (recog_memoized (used) < 0)
cost = 0;
else
{
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
if (INSN_CODE (insn) >= 0)
{
if (REG_NOTE_KIND (link) == REG_DEP_ANTI)
cost = 0;
else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
{
cost = (insn_default_latency (insn)
- insn_default_latency (used));
if (cost <= 0)
cost = 1;
}
else if (bypass_p (insn))
cost = insn_latency (insn, used);
}
}
if (targetm.sched.adjust_cost)
cost = (*targetm.sched.adjust_cost) (used, link, insn, cost);
if (cost < 0)
cost = 0;
}
return cost;
}
static int
priority (insn)
rtx insn;
{
rtx link;
if (! INSN_P (insn))
return 0;
if (! INSN_PRIORITY_KNOWN (insn))
{
int this_priority = 0;
if (INSN_DEPEND (insn) == 0)
this_priority = insn_cost (insn, 0, 0);
else
{
for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
{
rtx next;
int next_priority;
if (RTX_INTEGRATED_P (link))
continue;
next = XEXP (link, 0);
if (! (*current_sched_info->contributes_to_priority) (next, insn))
continue;
next_priority = insn_cost (insn, link, next) + priority (next);
if (next_priority > this_priority)
this_priority = next_priority;
}
}
INSN_PRIORITY (insn) = this_priority;
INSN_PRIORITY_KNOWN (insn) = 1;
}
return INSN_PRIORITY (insn);
}
#define SCHED_SORT(READY, N_READY) \
do { if ((N_READY) == 2) \
swap_sort (READY, N_READY); \
else if ((N_READY) > 2) \
qsort (READY, N_READY, sizeof (rtx), rank_for_schedule); } \
while (0)
static int
rank_for_schedule (x, y)
const PTR x;
const PTR y;
{
rtx tmp = *(const rtx *) y;
rtx tmp2 = *(const rtx *) x;
rtx link;
int tmp_class, tmp2_class, depend_count1, depend_count2;
int val, priority_val, weight_val, info_val;
if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
return SCHED_GROUP_P (tmp2) ? 1 : -1;
#if 1 && MAX_MULTIPLICITY > 1
{
int pri = INSN_PRIORITY(tmp);
int pri2 = INSN_PRIORITY(tmp2);
if ( insn_unit(tmp)>0 && function_units[insn_unit(tmp)].multiplicity>1 && INSN_COST(tmp)==1 )
pri -= 1;
if ( insn_unit(tmp2)>0 && function_units[insn_unit(tmp2)].multiplicity>1 && INSN_COST(tmp2)==1 )
pri2 -= 1;
priority_val = pri2 - pri;
}
#else
priority_val = INSN_PRIORITY (tmp2) - INSN_PRIORITY (tmp);
#endif
if (priority_val)
return priority_val;
if (!reload_completed &&
(weight_val = INSN_REG_WEIGHT (tmp) - INSN_REG_WEIGHT (tmp2)))
return weight_val;
info_val = (*current_sched_info->rank) (tmp, tmp2);
if (info_val)
return info_val;
if (last_scheduled_insn)
{
link = find_insn_list (tmp, INSN_DEPEND (last_scheduled_insn));
if (link == 0 || insn_cost (last_scheduled_insn, link, tmp) == 1)
tmp_class = 3;
else if (REG_NOTE_KIND (link) == 0)
tmp_class = 1;
else
tmp_class = 2;
link = find_insn_list (tmp2, INSN_DEPEND (last_scheduled_insn));
if (link == 0 || insn_cost (last_scheduled_insn, link, tmp2) == 1)
tmp2_class = 3;
else if (REG_NOTE_KIND (link) == 0)
tmp2_class = 1;
else
tmp2_class = 2;
if ((val = tmp2_class - tmp_class))
return val;
}
depend_count1 = 0;
for (link = INSN_DEPEND (tmp); link; link = XEXP (link, 1))
depend_count1++;
depend_count2 = 0;
for (link = INSN_DEPEND (tmp2); link; link = XEXP (link, 1))
depend_count2++;
val = depend_count2 - depend_count1;
if (val)
return val;
return INSN_LUID (tmp) - INSN_LUID (tmp2);
}
HAIFA_INLINE static void
swap_sort (a, n)
rtx *a;
int n;
{
rtx insn = a[n - 1];
int i = n - 2;
while (i >= 0 && rank_for_schedule (a + i, &insn) >= 0)
{
a[i + 1] = a[i];
i -= 1;
}
a[i + 1] = insn;
}
HAIFA_INLINE static void
queue_insn (insn, n_cycles)
rtx insn;
int n_cycles;
{
int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
rtx link = alloc_INSN_LIST (insn, insn_queue[next_q]);
insn_queue[next_q] = link;
q_size += 1;
if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\t\tReady-->Q: insn %s: ",
(*current_sched_info->print_insn) (insn, 0));
fprintf (sched_dump, "queued for %d cycles.\n", n_cycles);
}
}
HAIFA_INLINE static rtx *
ready_lastpos (ready)
struct ready_list *ready;
{
if (ready->n_ready == 0)
abort ();
return ready->vec + ready->first - ready->n_ready + 1;
}
HAIFA_INLINE void
ready_add (ready, insn)
struct ready_list *ready;
rtx insn;
{
if (ready->first == ready->n_ready)
{
memmove (ready->vec + ready->veclen - ready->n_ready,
ready_lastpos (ready),
ready->n_ready * sizeof (rtx));
ready->first = ready->veclen - 1;
}
ready->vec[ready->first - ready->n_ready] = insn;
ready->n_ready++;
}
HAIFA_INLINE static rtx
ready_remove_first (ready)
struct ready_list *ready;
{
rtx t;
if (ready->n_ready == 0)
abort ();
t = ready->vec[ready->first--];
ready->n_ready--;
if (ready->n_ready == 0)
ready->first = ready->veclen - 1;
return t;
}
HAIFA_INLINE static rtx
ready_element (ready, index)
struct ready_list *ready;
int index;
{
#ifdef ENABLE_CHECKING
if (ready->n_ready == 0 || index >= ready->n_ready)
abort ();
#endif
return ready->vec[ready->first - index];
}
HAIFA_INLINE static rtx
ready_remove (ready, index)
struct ready_list *ready;
int index;
{
rtx t;
int i;
if (index == 0)
return ready_remove_first (ready);
if (ready->n_ready == 0 || index >= ready->n_ready)
abort ();
t = ready->vec[ready->first - index];
ready->n_ready--;
for (i = index; i < ready->n_ready; i++)
ready->vec[ready->first - i] = ready->vec[ready->first - i - 1];
return t;
}
HAIFA_INLINE static void
ready_sort (ready)
struct ready_list *ready;
{
rtx *first = ready_lastpos (ready);
SCHED_SORT (first, ready->n_ready);
}
HAIFA_INLINE static void
adjust_priority (prev)
rtx prev;
{
if (targetm.sched.adjust_priority)
INSN_PRIORITY (prev) =
(*targetm.sched.adjust_priority) (prev, INSN_PRIORITY (prev));
}
HAIFA_INLINE static void
advance_one_cycle ()
{
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
if (targetm.sched.dfa_pre_cycle_insn)
state_transition (curr_state,
(*targetm.sched.dfa_pre_cycle_insn) ());
state_transition (curr_state, NULL);
if (targetm.sched.dfa_post_cycle_insn)
state_transition (curr_state,
(*targetm.sched.dfa_post_cycle_insn) ());
}
}
static int last_clock_var;
static int
schedule_insn (insn, ready, clock)
rtx insn;
struct ready_list *ready;
int clock;
{
rtx link;
int advance = 0;
int unit = 0;
if (!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
unit = insn_unit (insn);
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ()
&& sched_verbose >= 1)
{
char buf[2048];
print_insn (buf, insn, 0);
buf[40]=0;
fprintf (sched_dump, ";;\t%3i--> %-40s:", clock, buf);
if (recog_memoized (insn) < 0)
fprintf (sched_dump, "nothing");
else
print_reservation (sched_dump, insn);
fputc ('\n', sched_dump);
}
else if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\t\t--> scheduling insn <<<%d>>> on unit ",
INSN_UID (insn));
insn_print_units (insn);
fputc ('\n', sched_dump);
}
if (!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
{
if (sched_verbose && unit == -1)
visualize_no_unit (insn);
if (MAX_BLOCKAGE > 1 || issue_rate > 1 || sched_verbose)
schedule_unit (unit, insn, clock);
if (INSN_DEPEND (insn) == 0)
return 0;
}
for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
{
rtx next = XEXP (link, 0);
int cost = insn_cost (insn, link, next);
INSN_TICK (next) = MAX (INSN_TICK (next), clock + cost);
if ((INSN_DEP_COUNT (next) -= 1) == 0)
{
int effective_cost = INSN_TICK (next) - clock;
if (! (*current_sched_info->new_ready) (next))
continue;
if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\t\tdependences resolved: insn %s ",
(*current_sched_info->print_insn) (next, 0));
if (effective_cost < 1)
fprintf (sched_dump, "into ready\n");
else
fprintf (sched_dump, "into queue with cost=%d\n",
effective_cost);
}
adjust_priority (next);
if (effective_cost < 1)
ready_add (ready, next);
else
{
queue_insn (next, effective_cost);
if (SCHED_GROUP_P (next) && advance < effective_cost)
advance = effective_cost;
}
}
}
if (issue_rate > 1
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
{
if (reload_completed)
PUT_MODE (insn, clock > last_clock_var ? TImode : VOIDmode);
last_clock_var = clock;
}
return advance;
}
static rtx
unlink_other_notes (insn, tail)
rtx insn, tail;
{
rtx prev = PREV_INSN (insn);
while (insn != tail && GET_CODE (insn) == NOTE)
{
rtx next = NEXT_INSN (insn);
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_END)
{
PREV_INSN (insn) = note_list;
if (note_list)
NEXT_INSN (note_list) = insn;
note_list = insn;
}
insn = next;
}
return insn;
}
static rtx
unlink_line_notes (insn, tail)
rtx insn, tail;
{
rtx prev = PREV_INSN (insn);
while (insn != tail && GET_CODE (insn) == NOTE)
{
rtx next = NEXT_INSN (insn);
if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0)
{
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
LINE_NOTE (insn) = insn;
}
else
prev = insn;
insn = next;
}
return insn;
}
void
get_block_head_tail (b, headp, tailp)
int b;
rtx *headp;
rtx *tailp;
{
rtx head = BLOCK_HEAD (b);
rtx tail = BLOCK_END (b);
while (head != tail)
{
if (GET_CODE (head) == NOTE)
head = NEXT_INSN (head);
else if (GET_CODE (tail) == NOTE)
tail = PREV_INSN (tail);
else if (GET_CODE (head) == CODE_LABEL)
head = NEXT_INSN (head);
else
break;
}
*headp = head;
*tailp = tail;
}
int
no_real_insns_p (head, tail)
rtx head, tail;
{
while (head != NEXT_INSN (tail))
{
if (GET_CODE (head) != NOTE && GET_CODE (head) != CODE_LABEL)
return 0;
head = NEXT_INSN (head);
}
return 1;
}
void
rm_line_notes (head, tail)
rtx head, tail;
{
rtx next_tail;
rtx insn;
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev;
if (GET_CODE (insn) == NOTE)
{
prev = insn;
insn = unlink_line_notes (insn, next_tail);
if (prev == tail)
abort ();
if (prev == head)
abort ();
if (insn == next_tail)
abort ();
}
}
}
void
save_line_notes (b, head, tail)
int b;
rtx head, tail;
{
rtx next_tail;
rtx line = line_note_head[b];
rtx insn;
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
else
LINE_NOTE (insn) = line;
}
void
restore_line_notes (head, tail)
rtx head, tail;
{
rtx line, note, prev, new;
int added_notes = 0;
rtx next_tail, insn;
head = head;
next_tail = NEXT_INSN (tail);
for (line = head; line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
break;
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
else if (GET_CODE (insn) != NOTE
&& INSN_UID (insn) < old_max_uid
&& (note = LINE_NOTE (insn)) != 0
&& note != line
&& (line == 0
|| NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line)
|| NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line)))
{
line = note;
prev = PREV_INSN (insn);
if (LINE_NOTE (note))
{
LINE_NOTE (note) = 0;
PREV_INSN (note) = prev;
NEXT_INSN (prev) = note;
PREV_INSN (insn) = note;
NEXT_INSN (note) = insn;
}
else
{
added_notes++;
new = emit_note_after (NOTE_LINE_NUMBER (note), prev);
NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note);
RTX_INTEGRATED_P (new) = RTX_INTEGRATED_P (note);
}
}
if (sched_verbose && added_notes)
fprintf (sched_dump, ";; added %d line-number notes\n", added_notes);
}
void
rm_redundant_line_notes ()
{
rtx line = 0;
rtx insn = get_insns ();
int active_insn = 0;
int notes = 0;
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
{
if (active_insn == 0)
{
notes++;
NOTE_SOURCE_FILE (insn) = 0;
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
}
else if (line
&& NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn)
&& NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn))
{
notes++;
NOTE_SOURCE_FILE (line) = 0;
NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED;
line = insn;
}
else
line = insn;
active_insn = 0;
}
else if (!((GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
|| (GET_CODE (insn) == INSN
&& (GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER))))
active_insn++;
if (sched_verbose && notes)
fprintf (sched_dump, ";; deleted %d line-number notes\n", notes);
}
void
rm_other_notes (head, tail)
rtx head;
rtx tail;
{
rtx next_tail;
rtx insn;
note_list = 0;
if (head == tail && (! INSN_P (head)))
return;
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev;
if (GET_CODE (insn) == NOTE)
{
prev = insn;
insn = unlink_other_notes (insn, next_tail);
if (prev == tail)
abort ();
if (prev == head)
abort ();
if (insn == next_tail)
abort ();
}
}
}
static int
find_set_reg_weight (x)
rtx x;
{
if (GET_CODE (x) == CLOBBER
&& register_operand (SET_DEST (x), VOIDmode))
return 1;
if (GET_CODE (x) == SET
&& register_operand (SET_DEST (x), VOIDmode))
{
if (GET_CODE (SET_DEST (x)) == REG)
{
if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
return 1;
else
return 0;
}
return 1;
}
return 0;
}
static void
find_insn_reg_weight (b)
int b;
{
rtx insn, next_tail, head, tail;
get_block_head_tail (b, &head, &tail);
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
int reg_weight = 0;
rtx x;
if (! INSN_P (insn))
continue;
x = PATTERN (insn);
reg_weight += find_set_reg_weight (x);
if (GET_CODE (x) == PARALLEL)
{
int j;
for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
{
x = XVECEXP (PATTERN (insn), 0, j);
reg_weight += find_set_reg_weight (x);
}
}
for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
{
if (REG_NOTE_KIND (x) == REG_DEAD
|| REG_NOTE_KIND (x) == REG_UNUSED)
reg_weight--;
}
INSN_REG_WEIGHT (insn) = reg_weight;
}
}
static int clock_var;
static void
queue_to_ready (ready)
struct ready_list *ready;
{
rtx insn;
rtx link;
q_ptr = NEXT_Q (q_ptr);
for (link = insn_queue[q_ptr]; link; link = XEXP (link, 1))
{
insn = XEXP (link, 0);
q_size -= 1;
if (sched_verbose >= 2)
fprintf (sched_dump, ";;\t\tQ-->Ready: insn %s: ",
(*current_sched_info->print_insn) (insn, 0));
ready_add (ready, insn);
if (sched_verbose >= 2)
fprintf (sched_dump, "moving to ready without stalls\n");
}
insn_queue[q_ptr] = 0;
if (ready->n_ready == 0)
{
int stalls;
for (stalls = 1; stalls <= MAX_INSN_QUEUE_INDEX; stalls++)
{
if ((link = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
{
for (; link; link = XEXP (link, 1))
{
insn = XEXP (link, 0);
q_size -= 1;
if (sched_verbose >= 2)
fprintf (sched_dump, ";;\t\tQ-->Ready: insn %s: ",
(*current_sched_info->print_insn) (insn, 0));
ready_add (ready, insn);
if (sched_verbose >= 2)
fprintf (sched_dump, "moving to ready with %d stalls\n", stalls);
}
insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0;
advance_one_cycle ();
break;
}
advance_one_cycle ();
}
if ((!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
&& sched_verbose && stalls)
visualize_stall_cycles (stalls);
q_ptr = NEXT_Q_AFTER (q_ptr, stalls);
clock_var += stalls;
}
}
static void
debug_ready_list (ready)
struct ready_list *ready;
{
rtx *p;
int i;
if (ready->n_ready == 0)
{
fprintf (sched_dump, "\n");
return;
}
p = ready_lastpos (ready);
for (i = 0; i < ready->n_ready; i++)
fprintf (sched_dump, " %s", (*current_sched_info->print_insn) (p[i], 0));
fprintf (sched_dump, "\n");
}
static rtx
move_insn1 (insn, last)
rtx insn, last;
{
NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
NEXT_INSN (insn) = NEXT_INSN (last);
PREV_INSN (NEXT_INSN (last)) = insn;
NEXT_INSN (last) = insn;
PREV_INSN (insn) = last;
return insn;
}
static rtx
reemit_notes (insn, last)
rtx insn;
rtx last;
{
rtx note, retval;
retval = last;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_SAVE_NOTE)
{
enum insn_note note_type = INTVAL (XEXP (note, 0));
last = emit_note_before (note_type, last);
remove_note (insn, note);
note = XEXP (note, 1);
if (note_type == NOTE_INSN_EH_REGION_BEG
|| note_type == NOTE_INSN_EH_REGION_END)
NOTE_EH_HANDLER (last) = INTVAL (XEXP (note, 0));
remove_note (insn, note);
}
}
return retval;
}
static rtx
move_insn (insn, last)
rtx insn, last;
{
rtx retval = NULL;
move_insn1 (insn, last);
if (retval == NULL_RTX)
retval = reemit_notes (insn, insn);
else
reemit_notes (insn, insn);
SCHED_GROUP_P (insn) = 0;
return retval;
}
struct choice_entry
{
int index;
int rest;
int n;
state_t state;
};
static struct choice_entry *choice_stack;
static int cycle_issued_insns;
static int
max_issue (ready, index)
struct ready_list *ready;
int *index;
{
int n, i, all, n_ready, lookahead, best, delay;
struct choice_entry *top;
rtx insn;
lookahead = (*targetm.sched.first_cycle_multipass_dfa_lookahead) ();
best = 0;
memcpy (choice_stack->state, curr_state, dfa_state_size);
top = choice_stack;
top->rest = lookahead;
top->n = 0;
n_ready = ready->n_ready;
for (all = i = 0; i < n_ready; i++)
if (!ready_try [i])
all++;
i = 0;
for (;;)
{
if (top->rest == 0 || i >= n_ready)
{
if (top == choice_stack)
break;
if (best < top - choice_stack && ready_try [0])
{
best = top - choice_stack;
*index = choice_stack [1].index;
if (top->n == issue_rate - cycle_issued_insns || best == all)
break;
}
i = top->index;
ready_try [i] = 0;
top--;
memcpy (curr_state, top->state, dfa_state_size);
}
else if (!ready_try [i])
{
insn = ready_element (ready, i);
delay = state_transition (curr_state, insn);
if (delay < 0)
{
if (state_dead_lock_p (curr_state))
top->rest = 0;
else
top->rest--;
n = top->n;
if (memcmp (top->state, curr_state, dfa_state_size) != 0)
n++;
top++;
top->rest = lookahead;
top->index = i;
top->n = n;
memcpy (top->state, curr_state, dfa_state_size);
ready_try [i] = 1;
i = -1;
}
}
i++;
}
while (top != choice_stack)
{
ready_try [top->index] = 0;
top--;
}
memcpy (curr_state, choice_stack->state, dfa_state_size);
return best;
}
static rtx
choose_ready (ready)
struct ready_list *ready;
{
if (!targetm.sched.first_cycle_multipass_dfa_lookahead
|| (*targetm.sched.first_cycle_multipass_dfa_lookahead) () <= 0
|| SCHED_GROUP_P (ready_element (ready, 0)))
return ready_remove_first (ready);
else
{
int index, i;
rtx insn;
insn = ready_element (ready, 0);
if (INSN_CODE (insn) < 0)
return ready_remove_first (ready);
for (i = 1; i < ready->n_ready; i++)
{
insn = ready_element (ready, i);
ready_try [i]
= (INSN_CODE (insn) < 0
|| (targetm.sched.first_cycle_multipass_dfa_lookahead_guard
&& !(*targetm.sched.first_cycle_multipass_dfa_lookahead_guard) (insn)));
}
if (max_issue (ready, &index) == 0)
return ready_remove_first (ready);
else
return ready_remove (ready, index);
}
}
rtx
sched_emit_insn (pat)
rtx pat;
{
rtx insn = emit_insn_after (pat, last_scheduled_insn);
last_scheduled_insn = insn;
return insn;
}
void
schedule_block (b, rgn_n_insns)
int b;
int rgn_n_insns;
{
struct ready_list ready;
int i, first_cycle_insn_p;
int can_issue_more;
state_t temp_state = NULL;
int sort_p, advance, start_clock_var;
rtx prev_head = current_sched_info->prev_head;
rtx next_tail = current_sched_info->next_tail;
rtx head = NEXT_INSN (prev_head);
rtx tail = PREV_INSN (next_tail);
if (head == tail && (! INSN_P (head)))
abort ();
if (sched_verbose)
{
fprintf (sched_dump, ";; ======================================================\n");
fprintf (sched_dump,
";; -- basic block %d from %d to %d -- %s reload\n",
b, INSN_UID (head), INSN_UID (tail),
(reload_completed ? "after" : "before"));
fprintf (sched_dump, ";; ======================================================\n");
fprintf (sched_dump, "\n");
visualize_alloc ();
init_block_visualization ();
}
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
state_reset (curr_state);
else
clear_units ();
ready.veclen = rgn_n_insns + 1 + issue_rate;
ready.first = ready.veclen - 1;
ready.vec = (rtx *) xmalloc (ready.veclen * sizeof (rtx));
ready.n_ready = 0;
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
temp_state = alloca (dfa_state_size);
ready_try = (char *) xmalloc ((rgn_n_insns + 1) * sizeof (char));
memset (ready_try, 0, (rgn_n_insns + 1) * sizeof (char));
choice_stack
= (struct choice_entry *) xmalloc ((rgn_n_insns + 1)
* sizeof (struct choice_entry));
for (i = 0; i <= rgn_n_insns; i++)
choice_stack[i].state = (state_t) xmalloc (dfa_state_size);
}
(*current_sched_info->init_ready_list) (&ready);
if (targetm.sched.md_init)
(*targetm.sched.md_init) (sched_dump, sched_verbose, ready.veclen);
last_scheduled_insn = prev_head;
q_ptr = 0;
q_size = 0;
if (!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
max_insn_queue_index_macro_value = INSN_QUEUE_SIZE - 1;
else
max_insn_queue_index_macro_value = max_insn_queue_index;
insn_queue = (rtx *) alloca ((MAX_INSN_QUEUE_INDEX + 1) * sizeof (rtx));
memset ((char *) insn_queue, 0, (MAX_INSN_QUEUE_INDEX + 1) * sizeof (rtx));
last_clock_var = -1;
clock_var = -1;
advance = 0;
sort_p = TRUE;
while ((*current_sched_info->schedule_more_p) ())
{
do
{
start_clock_var = clock_var;
clock_var++;
advance_one_cycle ();
queue_to_ready (&ready);
if (ready.n_ready == 0)
abort ();
if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\t\tReady list after queue_to_ready: ");
debug_ready_list (&ready);
}
advance -= clock_var - start_clock_var;
}
while (advance > 0);
if (sort_p)
{
ready_sort (&ready);
if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\t\tReady list after ready_sort: ");
debug_ready_list (&ready);
}
}
if (targetm.sched.reorder
&& (ready.n_ready == 0
|| !SCHED_GROUP_P (ready_element (&ready, 0))))
can_issue_more =
(*targetm.sched.reorder) (sched_dump, sched_verbose,
ready_lastpos (&ready),
&ready.n_ready, clock_var);
else
can_issue_more = issue_rate;
first_cycle_insn_p = 1;
cycle_issued_insns = 0;
for (;;)
{
rtx insn;
int cost;
if (sched_verbose >= 2)
{
fprintf (sched_dump, ";;\tReady list (t =%3d): ",
clock_var);
debug_ready_list (&ready);
}
if (!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
{
if (ready.n_ready == 0 || !can_issue_more
|| !(*current_sched_info->schedule_more_p) ())
break;
insn = choose_ready (&ready);
cost = actual_hazard (insn_unit (insn), insn, clock_var, 0);
}
else
{
if (ready.n_ready == 0 || !can_issue_more
|| state_dead_lock_p (curr_state)
|| !(*current_sched_info->schedule_more_p) ())
break;
if (sort_p)
insn = choose_ready (&ready);
else
insn = ready_remove_first (&ready);
if (targetm.sched.dfa_new_cycle
&& (*targetm.sched.dfa_new_cycle) (sched_dump, sched_verbose,
insn, last_clock_var,
clock_var, &sort_p))
{
ready_add (&ready, insn);
break;
}
sort_p = TRUE;
memcpy (temp_state, curr_state, dfa_state_size);
if (recog_memoized (insn) < 0)
{
if (!first_cycle_insn_p
&& (GET_CODE (PATTERN (insn)) == ASM_INPUT
|| asm_noperands (PATTERN (insn)) >= 0))
cost = 1;
else
cost = 0;
}
else
{
cost = state_transition (temp_state, insn);
if (targetm.sched.first_cycle_multipass_dfa_lookahead
&& targetm.sched.dfa_bubble)
{
if (cost == 0)
{
int j;
rtx bubble;
for (j = 0;
(bubble = (*targetm.sched.dfa_bubble) (j))
!= NULL_RTX;
j++)
{
memcpy (temp_state, curr_state, dfa_state_size);
if (state_transition (temp_state, bubble) < 0
&& state_transition (temp_state, insn) < 0)
break;
}
if (bubble != NULL_RTX)
{
if (insert_schedule_bubbles_p)
{
rtx copy;
copy = copy_rtx (PATTERN (bubble));
emit_insn_after (copy, last_scheduled_insn);
last_scheduled_insn
= NEXT_INSN (last_scheduled_insn);
INSN_CODE (last_scheduled_insn)
= INSN_CODE (bubble);
PUT_MODE (last_scheduled_insn,
(clock_var > last_clock_var
? clock_var - last_clock_var
: VOIDmode));
last_clock_var = clock_var;
if (sched_verbose >= 2)
{
fprintf (sched_dump,
";;\t\t--> scheduling bubble insn <<<%d>>>:reservation ",
INSN_UID (last_scheduled_insn));
if (recog_memoized (last_scheduled_insn)
< 0)
fprintf (sched_dump, "nothing");
else
print_reservation
(sched_dump, last_scheduled_insn);
fprintf (sched_dump, "\n");
}
}
cost = -1;
}
}
}
if (cost < 0)
cost = 0;
else if (cost == 0)
cost = 1;
}
}
if (cost >= 1)
{
queue_insn (insn, cost);
continue;
}
if (! (*current_sched_info->can_schedule_ready_p) (insn))
goto next;
last_scheduled_insn = move_insn (insn, last_scheduled_insn);
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
if (memcmp (curr_state, temp_state, dfa_state_size) != 0)
cycle_issued_insns++;
memcpy (curr_state, temp_state, dfa_state_size);
}
if (targetm.sched.variable_issue)
can_issue_more =
(*targetm.sched.variable_issue) (sched_dump, sched_verbose,
insn, can_issue_more);
else if (GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
can_issue_more--;
advance = schedule_insn (insn, &ready, clock_var);
if (advance != 0)
break;
next:
first_cycle_insn_p = 0;
if (ready.n_ready > 0)
ready_sort (&ready);
if (targetm.sched.reorder2
&& (ready.n_ready == 0
|| !SCHED_GROUP_P (ready_element (&ready, 0))))
{
can_issue_more =
(*targetm.sched.reorder2) (sched_dump, sched_verbose,
ready.n_ready
? ready_lastpos (&ready) : NULL,
&ready.n_ready, clock_var);
}
}
if ((!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
&& sched_verbose)
visualize_scheduled_insns (clock_var);
}
if (targetm.sched.md_finish)
(*targetm.sched.md_finish) (sched_dump, sched_verbose);
if (sched_verbose)
{
fprintf (sched_dump, ";;\tReady list (final): ");
debug_ready_list (&ready);
if (!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
print_block_visualization ("");
}
if (current_sched_info->queue_must_finish_empty && q_size != 0)
abort ();
head = NEXT_INSN (prev_head);
tail = last_scheduled_insn;
if (!reload_completed)
{
rtx insn, link, next;
for (insn = head; insn != tail; insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
{
next = XEXP (link, 0);
INSN_TICK (next) -= clock_var;
}
}
}
if (note_list != 0)
{
rtx note_head = note_list;
while (PREV_INSN (note_head))
{
note_head = PREV_INSN (note_head);
}
PREV_INSN (note_head) = PREV_INSN (head);
NEXT_INSN (PREV_INSN (head)) = note_head;
PREV_INSN (head) = note_list;
NEXT_INSN (note_list) = head;
head = note_head;
}
if (sched_verbose)
{
fprintf (sched_dump, ";; total time = %d\n;; new head = %d\n",
clock_var, INSN_UID (head));
fprintf (sched_dump, ";; new tail = %d\n\n",
INSN_UID (tail));
visualize_free ();
}
current_sched_info->head = head;
current_sched_info->tail = tail;
free (ready.vec);
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
free (ready_try);
for (i = 0; i <= rgn_n_insns; i++)
free (choice_stack [i].state);
free (choice_stack);
}
}
int
set_priorities (head, tail)
rtx head, tail;
{
rtx insn;
int n_insn;
rtx prev_head;
prev_head = PREV_INSN (head);
if (head == tail && (! INSN_P (head)))
return 0;
n_insn = 0;
for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
continue;
n_insn++;
(void) priority (insn);
}
return n_insn;
}
void
sched_init (dump_file)
FILE *dump_file;
{
int luid;
basic_block b;
rtx insn;
int i;
#ifdef HAVE_cc0
flag_schedule_speculative_load = 0;
#endif
sched_verbose = sched_verbose_param;
if (sched_verbose_param == 0 && dump_file)
sched_verbose = 1;
sched_dump = ((sched_verbose_param >= 10 || !dump_file)
? stderr : dump_file);
if (targetm.sched.issue_rate)
issue_rate = (*targetm.sched.issue_rate) ();
else
issue_rate = 1;
old_max_uid = get_max_uid () + 1;
h_i_d = (struct haifa_insn_data *) xcalloc (old_max_uid, sizeof (*h_i_d));
for (i = 0; i < old_max_uid; i++)
h_i_d [i].cost = -1;
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
if (targetm.sched.init_dfa_pre_cycle_insn)
(*targetm.sched.init_dfa_pre_cycle_insn) ();
if (targetm.sched.init_dfa_post_cycle_insn)
(*targetm.sched.init_dfa_post_cycle_insn) ();
if (targetm.sched.first_cycle_multipass_dfa_lookahead
&& targetm.sched.init_dfa_bubbles)
(*targetm.sched.init_dfa_bubbles) ();
dfa_start ();
dfa_state_size = state_size ();
curr_state = xmalloc (dfa_state_size);
}
h_i_d[0].luid = 0;
luid = 1;
FOR_EACH_BB (b)
for (insn = b->head;; insn = NEXT_INSN (insn))
{
INSN_LUID (insn) = luid;
if (GET_CODE (insn) != NOTE)
++luid;
if (insn == b->end)
break;
}
init_dependency_caches (luid);
init_alias_analysis ();
if (write_symbols != NO_DEBUG)
{
rtx line;
line_note_head = (rtx *) xcalloc (last_basic_block, sizeof (rtx));
FOR_EACH_BB (b)
{
for (line = b->head; line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
{
line_note_head[b->index] = line;
break;
}
for (line = b->head; line; line = NEXT_INSN (line))
{
if (INSN_P (line))
break;
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
line_note_head[b->index] = line;
}
}
}
if ((!targetm.sched.use_dfa_pipeline_interface
|| !(*targetm.sched.use_dfa_pipeline_interface) ())
&& sched_verbose)
init_target_units ();
insn = EXIT_BLOCK_PTR->prev_bb->end;
if (NEXT_INSN (insn) == 0
|| (GET_CODE (insn) != NOTE
&& GET_CODE (insn) != CODE_LABEL
&& GET_CODE (NEXT_INSN (insn)) != BARRIER))
{
emit_note_after (NOTE_INSN_DELETED, EXIT_BLOCK_PTR->prev_bb->end);
EXIT_BLOCK_PTR->prev_bb->end = PREV_INSN (EXIT_BLOCK_PTR->prev_bb->end);
}
FOR_EACH_BB_REVERSE (b)
find_insn_reg_weight (b->index);
}
void
sched_finish ()
{
free (h_i_d);
if (targetm.sched.use_dfa_pipeline_interface
&& (*targetm.sched.use_dfa_pipeline_interface) ())
{
free (curr_state);
dfa_finish ();
}
free_dependency_caches ();
end_alias_analysis ();
if (write_symbols != NO_DEBUG)
free (line_note_head);
}
#endif