#include "debug.h"
#include "flags.h"
#include "df.h"
+/* ??? Need to add a dependency between m68k.o and sched-int.h. */
+#include "sched-int.h"
+#include "insn-codes.h"
enum reg_class regno_reg_class[] =
{
int scale;
};
+static int m68k_sched_adjust_cost (rtx, rtx, rtx, int);
+static int m68k_sched_variable_issue (FILE *, int, rtx, int);
+static void m68k_sched_md_init_global (FILE *, int, int);
+static void m68k_sched_md_finish_global (FILE *, int);
+static void m68k_sched_md_init (FILE *, int, int);
+static void m68k_sched_dfa_pre_advance_cycle (void);
+static void m68k_sched_dfa_post_advance_cycle (void);
+
static bool m68k_handle_option (size_t, const char *, int);
static rtx find_addr_reg (rtx);
static const char *singlemove_string (rtx *);
#undef TARGET_ASM_FILE_START_APP_OFF
#define TARGET_ASM_FILE_START_APP_OFF true
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST m68k_sched_adjust_cost
+
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE m68k_sched_variable_issue
+
+#undef TARGET_SCHED_INIT_GLOBAL
+#define TARGET_SCHED_INIT_GLOBAL m68k_sched_md_init_global
+
+#undef TARGET_SCHED_FINISH_GLOBAL
+#define TARGET_SCHED_FINISH_GLOBAL m68k_sched_md_finish_global
+
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT m68k_sched_md_init
+
+#undef TARGET_SCHED_DFA_PRE_ADVANCE_CYCLE
+#define TARGET_SCHED_DFA_PRE_ADVANCE_CYCLE m68k_sched_dfa_pre_advance_cycle
+
+#undef TARGET_SCHED_DFA_POST_ADVANCE_CYCLE
+#define TARGET_SCHED_DFA_POST_ADVANCE_CYCLE m68k_sched_dfa_post_advance_cycle
+
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION m68k_handle_option
{
m68k_symbolic_call_var = M68K_SYMBOLIC_CALL_JSR;
-#if MOTOROLA && !defined (USE_GAS)
- m68k_symbolic_jump = "jmp %a0";
-#else
m68k_symbolic_jump = "jra %a0";
-#endif
}
else if (TARGET_ID_SHARED_LIBRARY)
/* All addresses must be loaded from the GOT. */
if (TARGET_ISAC)
/* No unconditional long branch */;
else if (TARGET_PCREL)
- m68k_symbolic_jump = "bra.l %c0";
+ m68k_symbolic_jump = "bra%.l %c0";
else
- {
-#if defined(USE_GAS)
- m68k_symbolic_jump = "bra.l %p0";
-#else
- m68k_symbolic_jump = "bra %p0";
-#endif
- }
+ m68k_symbolic_jump = "bra%.l %p0";
/* Turn off function cse if we are doing PIC. We always want
function call to be done as `bsr foo@PLTPC'. */
/* ??? It's traditional to do this for -mpcrel too, but it isn't
switch (m68k_symbolic_call_var)
{
case M68K_SYMBOLIC_CALL_JSR:
-#if MOTOROLA && !defined (USE_GAS)
m68k_symbolic_call = "jsr %a0";
-#else
- m68k_symbolic_call = "jbsr %a0";
-#endif
break;
case M68K_SYMBOLIC_CALL_BSR_C:
- m68k_symbolic_call = "bsr.l %c0";
+ m68k_symbolic_call = "bsr%.l %c0";
break;
case M68K_SYMBOLIC_CALL_BSR_P:
-#if defined(USE_GAS)
- m68k_symbolic_call = "bsr.l %p0";
-#else
- m68k_symbolic_call = "bsr %p0";
-#endif
+ m68k_symbolic_call = "bsr%.l %p0";
break;
case M68K_SYMBOLIC_CALL_NONE:
gcc_unreachable ();
}
+#ifndef ASM_OUTPUT_ALIGN_WITH_NOP
+ if (align_labels > 2)
+ {
+ warning (0, "-falign-labels=%d is not supported", align_labels);
+ align_labels = 0;
+ }
+ if (align_loops > 2)
+ {
+ warning (0, "-falign-loops=%d is not supported", align_loops);
+ align_loops = 0;
+ }
+#endif
+
SUBTARGET_OVERRIDE_OPTIONS;
/* Setup scheduling options. */
- if (TUNE_CFV2)
- m68k_sched_cpu = CPU_CF_V2;
+ if (TUNE_CFV1)
+ m68k_sched_cpu = CPU_CFV1;
+ else if (TUNE_CFV2)
+ m68k_sched_cpu = CPU_CFV2;
+ else if (TUNE_CFV3)
+ m68k_sched_cpu = CPU_CFV3;
else
{
m68k_sched_cpu = CPU_UNKNOWN;
flag_schedule_insns_after_reload = 0;
flag_modulo_sched = 0;
}
+
+ if (m68k_sched_cpu != CPU_UNKNOWN)
+ {
+ if ((m68k_cpu_flags & (FL_CF_EMAC | FL_CF_EMAC_B)) != 0)
+ m68k_sched_mac = MAC_CF_EMAC;
+ else if ((m68k_cpu_flags & FL_CF_MAC) != 0)
+ m68k_sched_mac = MAC_CF_MAC;
+ else
+ m68k_sched_mac = MAC_NO;
+ }
}
/* Generate a macro of the form __mPREFIX_cpu_NAME, where PREFIX is the
switch (GET_CODE (operands[3]))
{
case EQ:
- output_asm_insn (MOTOROLA
- ? "dbeq %0,%l1\n\tjbeq %l2"
- : "dbeq %0,%l1\n\tjeq %l2",
- operands);
+ output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands);
break;
case NE:
- output_asm_insn (MOTOROLA
- ? "dbne %0,%l1\n\tjbne %l2"
- : "dbne %0,%l1\n\tjne %l2",
- operands);
+ output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands);
break;
case GT:
- output_asm_insn (MOTOROLA
- ? "dbgt %0,%l1\n\tjbgt %l2"
- : "dbgt %0,%l1\n\tjgt %l2",
- operands);
+ output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands);
break;
case GTU:
- output_asm_insn (MOTOROLA
- ? "dbhi %0,%l1\n\tjbhi %l2"
- : "dbhi %0,%l1\n\tjhi %l2",
- operands);
+ output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands);
break;
case LT:
- output_asm_insn (MOTOROLA
- ? "dblt %0,%l1\n\tjblt %l2"
- : "dblt %0,%l1\n\tjlt %l2",
- operands);
+ output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands);
break;
case LTU:
- output_asm_insn (MOTOROLA
- ? "dbcs %0,%l1\n\tjbcs %l2"
- : "dbcs %0,%l1\n\tjcs %l2",
- operands);
+ output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands);
break;
case GE:
- output_asm_insn (MOTOROLA
- ? "dbge %0,%l1\n\tjbge %l2"
- : "dbge %0,%l1\n\tjge %l2",
- operands);
+ output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands);
break;
case GEU:
- output_asm_insn (MOTOROLA
- ? "dbcc %0,%l1\n\tjbcc %l2"
- : "dbcc %0,%l1\n\tjcc %l2",
- operands);
+ output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands);
break;
case LE:
- output_asm_insn (MOTOROLA
- ? "dble %0,%l1\n\tjble %l2"
- : "dble %0,%l1\n\tjle %l2",
- operands);
+ output_asm_insn ("dble %0,%l1\n\tjle %l2", operands);
break;
case LEU:
- output_asm_insn (MOTOROLA
- ? "dbls %0,%l1\n\tjbls %l2"
- : "dbls %0,%l1\n\tjls %l2",
- operands);
+ output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands);
break;
default:
switch (GET_MODE (operands[0]))
{
case SImode:
- output_asm_insn (MOTOROLA
- ? "clr%.w %0\n\tsubq%.l #1,%0\n\tjbpl %l1"
- : "clr%.w %0\n\tsubq%.l #1,%0\n\tjpl %l1",
- operands);
+ output_asm_insn ("clr%.w %0\n\tsubq%.l #1,%0\n\tjpl %l1", operands);
break;
case HImode:
}
loperands[4] = gen_label_rtx ();
if (operand2 != const0_rtx)
- {
- output_asm_insn (MOTOROLA
- ? "cmp%.l %2,%0\n\tjbne %l4\n\tcmp%.l %3,%1"
- : "cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1",
- loperands);
- }
+ output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands);
else
{
if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[0]))
else
output_asm_insn ("cmp%.w #0,%0", loperands);
- output_asm_insn (MOTOROLA ? "jbne %l4" : "jne %l4", loperands);
+ output_asm_insn ("jne %l4", loperands);
if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[1]))
output_asm_insn ("tst%.l %1", loperands);
case GT:
loperands[6] = gen_label_rtx ();
- output_asm_insn (MOTOROLA ? "shi %5\n\tjbra %l6" : "shi %5\n\tjra %l6",
- loperands);
+ output_asm_insn ("shi %5\n\tjra %l6", loperands);
(*targetm.asm_out.internal_label) (asm_out_file, "L",
CODE_LABEL_NUMBER (loperands[4]));
output_asm_insn ("sgt %5", loperands);
case LT:
loperands[6] = gen_label_rtx ();
- output_asm_insn (MOTOROLA ? "scs %5\n\tjbra %l6" : "scs %5\n\tjra %l6",
- loperands);
+ output_asm_insn ("scs %5\n\tjra %l6", loperands);
(*targetm.asm_out.internal_label) (asm_out_file, "L",
CODE_LABEL_NUMBER (loperands[4]));
output_asm_insn ("slt %5", loperands);
case GE:
loperands[6] = gen_label_rtx ();
- output_asm_insn (MOTOROLA ? "scc %5\n\tjbra %l6" : "scc %5\n\tjra %l6",
- loperands);
+ output_asm_insn ("scc %5\n\tjra %l6", loperands);
(*targetm.asm_out.internal_label) (asm_out_file, "L",
CODE_LABEL_NUMBER (loperands[4]));
output_asm_insn ("sge %5", loperands);
case LE:
loperands[6] = gen_label_rtx ();
- output_asm_insn (MOTOROLA ? "sls %5\n\tjbra %l6" : "sls %5\n\tjra %l6",
- loperands);
+ output_asm_insn ("sls %5\n\tjra %l6", loperands);
(*targetm.asm_out.internal_label) (asm_out_file, "L",
CODE_LABEL_NUMBER (loperands[4]));
output_asm_insn ("sle %5", loperands);
}
else if (CONSTANT_P (operands[1]))
return "move%.l %1,%0";
- /* Recognize the insn before a tablejump, one that refers
- to a table of offsets. Such an insn will need to refer
- to a label on the insn. So output one. Use the label-number
- of the table of offsets to generate this label. This code,
- and similar code below, assumes that there will be at most one
- reference to each table. */
- if (GET_CODE (operands[1]) == MEM
- && GET_CODE (XEXP (operands[1], 0)) == PLUS
- && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == LABEL_REF
- && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != PLUS)
- {
- rtx labelref = XEXP (XEXP (operands[1], 0), 1);
- if (MOTOROLA)
- asm_fprintf (asm_out_file, "\t.set %LLI%d,.+2\n",
- CODE_LABEL_NUMBER (XEXP (labelref, 0)));
- else
- (*targetm.asm_out.internal_label) (asm_out_file, "LI",
- CODE_LABEL_NUMBER (XEXP (labelref, 0)));
- }
return "move%.w %1,%0";
}
if (FP_REGNO_P (REGNO (XEXP (XVECEXP (pattern, 0, first), store_p))))
{
if (store_p)
- return MOTOROLA ? "fmovm %1,%a0" : "fmovem %1,%a0";
+ return "fmovem %1,%a0";
else
- return MOTOROLA ? "fmovm %a0,%1" : "fmovem %a0,%1";
+ return "fmovem %a0,%1";
}
else
{
if (store_p)
- return MOTOROLA ? "movm.l %1,%a0" : "moveml %1,%a0";
+ return "movem%.l %1,%a0";
else
- return MOTOROLA ? "movm.l %a0,%1" : "moveml %a0,%1";
+ return "movem%.l %a0,%1";
}
}
if (((cc_status.value1 && FP_REG_P (cc_status.value1))
|| (cc_status.value2 && FP_REG_P (cc_status.value2))))
cc_status.flags = CC_IN_68881;
+ if (cc_status.value2 && GET_CODE (cc_status.value2) == COMPARE
+ && GET_MODE_CLASS (GET_MODE (XEXP (cc_status.value2, 0))) == MODE_FLOAT)
+ {
+ cc_status.flags = CC_IN_68881;
+ if (!FP_REG_P (XEXP (cc_status.value2, 0)))
+ cc_status.flags |= CC_REVERSED;
+ }
}
\f
const char *
It is possible for PIC to generate a (plus (label_ref...) (reg...))
and we handle that just like we would a (plus (symbol_ref...) (reg...)).
- Some SGS assemblers have a bug such that "Lnnn-LInnn-2.b(pc,d0.l*2)"
- fails to assemble. Luckily "Lnnn(pc,d0.l*2)" produces the results
- we want. This difference can be accommodated by using an assembler
- define such "LDnnn" to be either "Lnnn-LInnn-2.b", "Lnnn", or any other
- string, as necessary. This is accomplished via the ASM_OUTPUT_CASE_END
- macro. See m68k/sgs.h for an example; for versions without the bug.
- Some assemblers refuse all the above solutions. The workaround is to
- emit "K(pc,d0.l*2)" with K being a small constant known to give the
- right behavior.
-
- They also do not like things like "pea 1.w", so we simple leave off
- the .w on small constants.
-
This routine is responsible for distinguishing between -fpic and -fPIC
style relocations in an address. When generating -fpic code the
offset is output in word mode (e.g. movel a5@(_foo:w), a0). When generating
{
/* Print the "offset(base" component. */
if (labelno >= 0)
- asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,", labelno, labelno);
+ asm_fprintf (file, "%LL%d(%Rpc,", labelno);
else
{
if (address.offset)
{
/* Print the "base@(offset" component. */
if (labelno >= 0)
- asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b", labelno, labelno);
+ asm_fprintf (file, "%Rpc@(%LL%d", labelno);
else
{
if (address.base)
strict_low_part_peephole_ok (enum machine_mode mode, rtx first_insn,
rtx target)
{
- rtx p;
-
- p = prev_nonnote_insn (first_insn);
+ rtx p = first_insn;
- while (p)
+ while ((p = PREV_INSN (p)))
{
+ if (NOTE_INSN_BASIC_BLOCK_P (p))
+ return false;
+
+ if (NOTE_P (p))
+ continue;
+
/* If it isn't an insn, then give up. */
- if (GET_CODE (p) != INSN)
+ if (!INSN_P (p))
return false;
if (reg_set_p (target, p))
else
return false;
}
-
- p = prev_nonnote_insn (p);
}
return false;
/* Restore the original PIC register. */
if (flag_pic)
SET_REGNO (pic_offset_table_rtx, PIC_REG);
+ free_after_compilation (cfun);
}
/* Worker function for TARGET_STRUCT_VALUE_RTX. */
/* CPU to schedule the program for. */
enum attr_cpu m68k_sched_cpu;
+/* MAC to schedule the program for. */
+enum attr_mac m68k_sched_mac;
+
/* Operand type. */
enum attr_op_type
{
return type;
}
+
+/* Implement type2 attribute. */
+enum attr_type2
+m68k_sched_attr_type2 (rtx insn)
+{
+ switch (get_attr_type1 (insn))
+ {
+ case TYPE1_ALU_L:
+ case TYPE1_ALUQ_L:
+ case TYPE1_CMP_L:
+ return TYPE2_ALU;
+
+ case TYPE1_ALU_REG1:
+ case TYPE1_ALU_REGX:
+ return TYPE2_ALU_REG;
+
+ case TYPE1_BCC:
+ return TYPE2_BCC;
+
+ case TYPE1_BRA:
+ return TYPE2_BRA;
+
+ case TYPE1_BSR:
+ case TYPE1_JSR:
+ return TYPE2_CALL;
+
+ case TYPE1_JMP:
+ return TYPE2_JMP;
+
+ case TYPE1_LEA:
+ return TYPE2_LEA;
+
+ case TYPE1_CLR:
+ case TYPE1_MOV3Q_L:
+ case TYPE1_MOVE:
+ case TYPE1_MOVEQ_L:
+ case TYPE1_TST:
+ switch (m68k_sched_cpu)
+ {
+ case CPU_CFV1:
+ return TYPE2_OMOVE;
+
+ case CPU_CFV2:
+ case CPU_CFV3:
+ return TYPE2_ALU;
+
+ default:
+ gcc_assert (get_attr_guess (insn) == GUESS_YES);
+ return TYPE2_UNKNOWN;
+ }
+
+ case TYPE1_MUL_L:
+ return TYPE2_MUL_L;
+
+ case TYPE1_MUL_W:
+ return TYPE2_MUL_W;
+
+ case TYPE1_MOVE_L:
+ case TYPE1_TST_L:
+ return TYPE2_OMOVE;
+
+ case TYPE1_PEA:
+ return TYPE2_PEA;
+
+ case TYPE1_RTS:
+ return TYPE2_RTS;
+
+ case TYPE1_UNLK:
+ return TYPE2_UNLK;
+
+ default:
+ gcc_assert (get_attr_guess (insn) == GUESS_YES);
+ return TYPE2_UNKNOWN;
+ }
+}
+
+/* An empty state that is used in m68k_sched_adjust_cost. */
+static state_t sched_adjust_cost_state;
+
+/* Implement adjust_cost scheduler hook.
+ Return adjusted COST of dependency LINK between DEF_INSN and INSN. */
+static int
+m68k_sched_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx def_insn,
+ int cost)
+{
+ int delay;
+
+ if (recog_memoized (def_insn) < 0
+ || recog_memoized (insn) < 0)
+ return cost;
+
+ /* Don't try to issue INSN earlier than DFA permits.
+ This is especially useful for instructions that write to memory,
+ as their true dependence (default) latency is better to be set to 0
+ to workaround alias analysis limitations.
+ This is, in fact, a machine independent tweak, so, probably,
+ it should be moved to haifa-sched.c: insn_cost (). */
+
+ delay = min_insn_conflict_delay (sched_adjust_cost_state, def_insn, insn);
+ if (delay > cost)
+ cost = delay;
+
+ return cost;
+}
+
+/* Maximal length of instruction for current CPU.
+ E.g. it is 3 for any ColdFire core. */
+static int max_insn_size;
+
+/* Data to model instruction buffer of CPU. */
+struct _sched_ib
+{
+ /* Size of the instruction buffer in words. */
+ int size;
+
+ /* Number of filled words in the instruction buffer. */
+ int filled;
+
+ /* Additional information about instruction buffer for CPUs that have
+ a buffer of instruction records, rather then a plain buffer
+ of instruction words. */
+ struct _sched_ib_records
+ {
+ /* Size of buffer in records. */
+ int n_insns;
+
+ /* Array to hold data on adjustements made to the size of the buffer. */
+ int *adjust;
+
+ /* Index of the above array. */
+ int adjust_index;
+ } records;
+
+ /* An insn that reserves (marks empty) one word in the instruction buffer. */
+ rtx insn;
+};
+
+static struct _sched_ib sched_ib;
+
+/* ID of memory unit. */
+static int sched_mem_unit_code;
+
+/* Implementation of the targetm.sched.variable_issue () hook.
+ It is called after INSN was issued. It returns the number of insns
+ that can possibly get scheduled on the current cycle.
+ It is used here to determine the effect of INSN on the instruction
+ buffer. */
+static int
+m68k_sched_variable_issue (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx insn, int can_issue_more)
+{
+ int insn_size;
+
+ if (recog_memoized (insn) >= 0)
+ {
+ switch (m68k_sched_cpu)
+ {
+ case CPU_CFV1:
+ case CPU_CFV2:
+ insn_size = get_attr_size (insn);
+ break;
+
+ case CPU_CFV3:
+ insn_size = get_attr_size (insn);
+
+ /* ColdFire V3 and V4 cores have instruction buffers that can
+ accumulate up to 8 instructions regardless of instructions'
+ sizes. So we should take care not to "prefetch" 24 one-word
+ or 12 two-words instructions.
+ To model this behavior we temporarily decrease size of the
+ buffer by (max_insn_size - insn_size) for next 7 instructions. */
+ {
+ int adjust;
+
+ adjust = max_insn_size - insn_size;
+ sched_ib.size -= adjust;
+
+ if (sched_ib.filled > sched_ib.size)
+ sched_ib.filled = sched_ib.size;
+
+ sched_ib.records.adjust[sched_ib.records.adjust_index] = adjust;
+ }
+
+ ++sched_ib.records.adjust_index;
+ if (sched_ib.records.adjust_index == sched_ib.records.n_insns)
+ sched_ib.records.adjust_index = 0;
+
+ /* Undo adjustement we did 7 instructions ago. */
+ sched_ib.size
+ += sched_ib.records.adjust[sched_ib.records.adjust_index];
+
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ gcc_assert (insn_size <= sched_ib.filled);
+ --can_issue_more;
+ }
+ else if (GET_CODE (PATTERN (insn)) == ASM_INPUT
+ || asm_noperands (PATTERN (insn)) >= 0)
+ insn_size = sched_ib.filled;
+ else
+ insn_size = 0;
+
+ sched_ib.filled -= insn_size;
+
+ return can_issue_more;
+}
+
+/* Statistics gatherer. */
+
+typedef enum
+ {
+ /* Something needs to be done for this insn. */
+ SCHED_DUMP_TODO,
+
+ /* Support for this insn is complete. */
+ SCHED_DUMP_DONE,
+
+ /* This insn didn't require much effort to support it. */
+ SCHED_DUMP_NOTHING
+ } sched_dump_class_def;
+
+/* Pointer to functions that classifies insns into 3 above classes. */
+typedef sched_dump_class_def (*sched_dump_class_func_t) (rtx);
+
+/* Return statistical type of INSN regarding splits. */
+static sched_dump_class_def
+sched_dump_split_class (rtx insn)
+{
+ int i;
+
+ i = recog_memoized (insn);
+ gcc_assert (i >= 0);
+
+ switch (get_attr_split (insn))
+ {
+ case SPLIT_TODO:
+ return SCHED_DUMP_TODO;
+
+ case SPLIT_DONE:
+ return SCHED_DUMP_DONE;
+
+ case SPLIT_NOTHING:
+ return SCHED_DUMP_NOTHING;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* ID of the guess unit. */
+static int sched_dump_dfa_guess_unit_code;
+
+/* DFA state for use in sched_dump_dfa_class (). */
+static state_t sched_dump_dfa_state;
+
+/* Return statistical type of INSN regarding DFA reservations. */
+static sched_dump_class_def
+sched_dump_dfa_class (rtx insn)
+{
+ int i;
+
+ i = recog_memoized (insn);
+ gcc_assert (i >= 0 && insn_has_dfa_reservation_p (insn));
+
+ if (sched_dump_split_class (insn) == SCHED_DUMP_TODO)
+ /* Insn is not yet ready for reservations. */
+ return SCHED_DUMP_NOTHING;
+
+ state_reset (sched_dump_dfa_state);
+
+ if (state_transition (sched_dump_dfa_state, insn) >= 0)
+ gcc_unreachable ();
+
+ if (cpu_unit_reservation_p (sched_dump_dfa_state,
+ sched_dump_dfa_guess_unit_code))
+ return SCHED_DUMP_TODO;
+
+ return SCHED_DUMP_DONE;
+}
+
+/* Dump statistics on current function into file DUMP_FILENAME and prefix
+ each entry with PREFIX.
+ Instructions are classified with DUMP_CLASS. */
+static void
+m68k_sched_dump (sched_dump_class_func_t dump_class,
+ const char *prefix, FILE *dump)
+{
+ sbitmap present;
+ int *todos;
+ int *dones;
+ int *nothings;
+ rtx insn;
+
+ gcc_assert (dump != NULL);
+
+ present = sbitmap_alloc (CODE_FOR_nothing);
+ sbitmap_zero (present);
+
+ todos = xcalloc (CODE_FOR_nothing, sizeof (*todos));
+ dones = xcalloc (CODE_FOR_nothing, sizeof (*dones));
+ nothings = xcalloc (CODE_FOR_nothing, sizeof (*nothings));
+
+ /* Gather statistics. */
+ for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn) && recog_memoized (insn) >= 0)
+ {
+ enum insn_code code;
+
+ code = INSN_CODE (insn);
+ gcc_assert (code < CODE_FOR_nothing);
+
+ SET_BIT (present, code);
+
+ switch (dump_class (insn))
+ {
+ case SCHED_DUMP_TODO:
+ ++todos[code];
+ break;
+
+ case SCHED_DUMP_DONE:
+ ++dones[code];
+ break;
+
+ case SCHED_DUMP_NOTHING:
+ ++nothings[code];
+ break;
+ }
+ }
+ }
+
+ /* Print statisctics. */
+ {
+ unsigned int i;
+ sbitmap_iterator si;
+ int total_todo;
+ int total_done;
+ int total_nothing;
+
+ total_todo = 0;
+ total_done = 0;
+ total_nothing = 0;
+
+ EXECUTE_IF_SET_IN_SBITMAP (present, 0, i, si)
+ {
+ int todo;
+ int done;
+ int nothing;
+ enum insn_code code;
+
+ code = (enum insn_code) i;
+
+ todo = todos[code];
+ done = dones[code];
+ nothing = nothings[code];
+
+ total_todo += todo;
+ total_done += done;
+ total_nothing += nothing;
+
+ if (todo != 0)
+ {
+ fprintf (dump,
+ "%s: %3d: %d / %d / %d ;",
+ prefix, code, todo, done, nothing);
+
+ {
+ const char *name;
+
+ name = get_insn_name (code);
+
+ if (name != NULL)
+ fprintf (dump, " {%s}\n", name);
+ else
+ fprintf (dump, " {unknown}\n");
+ }
+ }
+ }
+
+ gcc_assert (CODE_FOR_nothing < 999);
+
+ fprintf (dump,
+ "%s: 999: %d / %d / %d ; {total}\n",
+ prefix, total_todo, total_done, total_nothing);
+ }
+
+ free (nothings);
+ nothings = NULL;
+ free (dones);
+ dones = NULL;
+ free (todos);
+ todos = NULL;
+
+ sbitmap_free (present);
+ present = NULL;
+}
+
+/* Implementation of targetm.sched.md_init_global () hook.
+ It is invoked once per scheduling pass and is used here
+ to initialize scheduler constants. */
+static void
+m68k_sched_md_init_global (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ int n_insns ATTRIBUTE_UNUSED)
+{
+ /* Init branch types. */
+ {
+ rtx insn;
+
+ sched_branch_type = xcalloc (get_max_uid () + 1,
+ sizeof (*sched_branch_type));
+
+ for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+ {
+ if (JUMP_P (insn))
+ /* !!! FIXME: Implement real scan here. */
+ sched_branch_type[INSN_UID (insn)] = TYPE_BCC;
+ }
+ }
+
+ if (reload_completed && sched_verbose >= 8)
+ /* Dump statistics. */
+ {
+ m68k_sched_dump (sched_dump_split_class, "m68k_sched_split",
+ sched_dump);
+
+ sched_dump_dfa_guess_unit_code = get_cpu_unit_code ("cf_guess");
+ sched_dump_dfa_state = alloca (state_size ());
+
+ m68k_sched_dump (sched_dump_dfa_class, "m68k_sched_dfa",
+ sched_dump);
+
+ sched_dump_dfa_state = NULL;
+ sched_dump_dfa_guess_unit_code = 0;
+ }
+
+ /* Setup target cpu. */
+ switch (m68k_sched_cpu)
+ {
+ case CPU_CFV1:
+ case CPU_CFV2:
+ max_insn_size = 3;
+ sched_ib.records.n_insns = 0;
+ sched_ib.records.adjust = NULL;
+ break;
+
+ case CPU_CFV3:
+ max_insn_size = 3;
+ sched_ib.records.n_insns = 8;
+ sched_ib.records.adjust = xmalloc (sched_ib.records.n_insns
+ * sizeof (*sched_ib.records.adjust));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ sched_mem_unit_code = get_cpu_unit_code ("cf_mem1");
+
+ sched_adjust_cost_state = xmalloc (state_size ());
+ state_reset (sched_adjust_cost_state);
+
+ start_sequence ();
+ emit_insn (gen_ib ());
+ sched_ib.insn = get_insns ();
+ end_sequence ();
+}
+
+/* Scheduling pass is now finished. Free/reset static variables. */
+static void
+m68k_sched_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED)
+{
+ sched_ib.insn = NULL;
+
+ free (sched_adjust_cost_state);
+ sched_adjust_cost_state = NULL;
+
+ sched_mem_unit_code = 0;
+
+ free (sched_ib.records.adjust);
+ sched_ib.records.adjust = NULL;
+ sched_ib.records.n_insns = 0;
+ max_insn_size = 0;
+
+ free (sched_branch_type);
+ sched_branch_type = NULL;
+}
+
+/* Implementation of targetm.sched.md_init () hook.
+ It is invoked each time scheduler starts on the new block (basic block or
+ extended basic block). */
+static void
+m68k_sched_md_init (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ int n_insns ATTRIBUTE_UNUSED)
+{
+ switch (m68k_sched_cpu)
+ {
+ case CPU_CFV1:
+ case CPU_CFV2:
+ sched_ib.size = 6;
+ break;
+
+ case CPU_CFV3:
+ sched_ib.size = sched_ib.records.n_insns * max_insn_size;
+
+ memset (sched_ib.records.adjust, 0,
+ sched_ib.records.n_insns * sizeof (*sched_ib.records.adjust));
+ sched_ib.records.adjust_index = 0;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* haifa-sched.c: schedule_block () calls advance_cycle () just before
+ the first cycle. Workaround that. */
+ sched_ib.filled = -2;
+}
+
+/* Implementation of targetm.sched.dfa_pre_advance_cycle () hook.
+ It is invoked just before current cycle finishes and is used here
+ to track if instruction buffer got its two words this cycle. */
+static void
+m68k_sched_dfa_pre_advance_cycle (void)
+{
+ if (!cpu_unit_reservation_p (curr_state, sched_mem_unit_code))
+ {
+ sched_ib.filled += 2;
+
+ if (sched_ib.filled > sched_ib.size)
+ sched_ib.filled = sched_ib.size;
+ }
+}
+
+/* Implementation of targetm.sched.dfa_post_advance_cycle () hook.
+ It is invoked just after new cycle begins and is used here
+ to setup number of filled words in the instruction buffer so that
+ instructions which won't have all their words prefetched would be
+ stalled for a cycle. */
+static void
+m68k_sched_dfa_post_advance_cycle (void)
+{
+ int i;
+
+ /* Setup number of prefetched instruction words in the instruction
+ buffer. */
+ i = max_insn_size - sched_ib.filled;
+
+ while (--i >= 0)
+ {
+ if (state_transition (curr_state, sched_ib.insn) >= 0)
+ gcc_unreachable ();
+ }
+}