X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fconfig%2Fm68k%2Fm68k.c;h=0c38fbdfd19659ae9e5fe357d94896feb9b88878;hp=952b94783d451bfd322ba9dee5a9692640ac8c38;hb=2ccdedfd0da7f3ea4b1b5d2f3ded07addcd69a4d;hpb=4ad53767b95256ab2e740072d8447218c6fe720c diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index 952b94783d4..0c38fbdfd19 100644 --- a/gcc/config/m68k/m68k.c +++ b/gcc/config/m68k/m68k.c @@ -43,6 +43,9 @@ along with GCC; see the file COPYING3. If not see #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[] = { @@ -118,6 +121,14 @@ struct m68k_address { 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 *); @@ -185,6 +196,27 @@ int m68k_last_compare_had_fp_operands; #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 @@ -634,8 +666,12 @@ override_options (void) 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; @@ -643,6 +679,16 @@ override_options (void) 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 @@ -4316,6 +4362,7 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, /* 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. */ @@ -4523,6 +4570,9 @@ m68k_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) /* 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 { @@ -4971,3 +5021,564 @@ m68k_sched_branch_type (rtx insn) 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 (); + } +}