diff options
author | spop <spop@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-09-09 23:11:36 +0000 |
---|---|---|
committer | spop <spop@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-09-09 23:11:36 +0000 |
commit | 0ac9454ea4a35a1c655a4dfbc73825be18cfc905 (patch) | |
tree | 1454bef85bf406eb8a5c96db18f42d3f309e3f46 /gcc | |
parent | 279dadf57ca2306e594ea2d4ab91f063b20107f5 (diff) | |
download | gcc-0ac9454ea4a35a1c655a4dfbc73825be18cfc905.tar.gz |
Dispatch scheduling for AMD Bulldozer processors.
2010-09-09 Reza Yazdani <reza.yazdani@amd.com>
* config/i386/i386.c: Include sched-int.h.
(TARGET_SCHED_DISPATCH): Defined.
(TARGET_SCHED_DISPATCH_DO): Defined.
(DISPATCH_WINDOW_SIZE): Defined.
(MAX_DISPATCH_WINDOWS): Defined.
(MAX_INSN): Defined.
(MAX_IMM): Defined.
(MAX_IMM_SIZE): Defined.
(MAX_IMM_32): Defined.
(MAX_IMM_64): Defined.
(MAX_LOAD): Defined.
(MAX_STORE): Defined.
(BIG): Defined.
(dispatch_group): New.
(num_allowable_groups): New.
(group_name): New.
(sched_insn_info_s): New.
(dispatch_windows_s): New.
(imm_info_s): New.
(dispatch_window_list): New.
(dispatch_window_list1): New.
(get_mem_group): New.
(is_cmp): New.
(dispatch_violation): New.
(is_branch): New.
(is_prefetch): New.
(init_window): New.
(allocate_window): New.
(init_dispatch_sched): New.
(is_end_basic_block): New.
(process_end_window): New.
(allocate_next_window): New.
(find_constant_1): New.
(find_constant): New.
(get_num_immediates): New.
(has_immediate): New.
(get_insn_path): New.
(get_insn_group): New.
(count_num_restricted): New.
(fits_dispatch_window): New.
(add_insn_window): New.
(add_to_dispatch_window): New.
(debug_dispatch_window_file): New.
(debug_dispatch_window): New.
(debug_insn_dispatch_info_file): New.
(debug_ready_dispatch): New.
(do_dispatch): New.
(has_dispatch): New.
* config/i386/i386.h (debug_ready_dispatch): Declared.
(debug_dispatch_window): Declared.
* config/i386/i386.opt (mdispatch-scheduler): New flag.
* doc/tm.texi.in (TARGET_SCHED_DISPATCH): New.
(TARGET_SCHED_DISPATCH_DO): New.
* doc/tm.texi: Regererated.
* haifa-sched.c (choose_ready): Call targetm.sched.dispatch and
ready_remove_first_dispatch
(schedule_block): Call targetm.sched.dispatch and
targetm.sched.dispatch_do.
(sched_init): Call targetm.sched.dispatch and
targetm.sched.dispatch_do.
(ready_remove_first_dispatch): New.
(number_in_ready): New.
(get_ready_element): New.
* hooks.c (hook_bool_rtx_int_false): New.
(hook_void_rtx_int): New.
* hooks.h (hook_bool_rtx_int_false): Declared.
(hook_void_rtx_int): Declared.
* sched-int.h (IS_DISPATCH_ON): Defined.
(IS_CMP): Defined.
(DISPATCH_VIOLATION): Defined.
(FITS_DISPATCH_WINDOW): Defined.
(DISPATCH_INIT): Defined.
(ADD_TO_DISPATCH_WINDOW): Defined.
(get_ready_element): Declared.
(number_in_ready): Declared.
* target.def (dispatch): Defined.
(dispatch_do): Defined.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@164133 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 80 | ||||
-rw-r--r-- | gcc/config/i386/i386.c | 802 | ||||
-rw-r--r-- | gcc/config/i386/i386.h | 3 | ||||
-rw-r--r-- | gcc/config/i386/i386.opt | 5 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 10 | ||||
-rw-r--r-- | gcc/doc/tm.texi.in | 10 | ||||
-rw-r--r-- | gcc/haifa-sched.c | 84 | ||||
-rw-r--r-- | gcc/hooks.c | 15 | ||||
-rw-r--r-- | gcc/hooks.h | 2 | ||||
-rw-r--r-- | gcc/sched-int.h | 9 | ||||
-rw-r--r-- | gcc/target.def | 18 |
11 files changed, 1036 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 781d5a1219a..17a253afca7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,83 @@ +2010-09-09 Reza Yazdani <reza.yazdani@amd.com> + + * config/i386/i386.c: Include sched-int.h. + (TARGET_SCHED_DISPATCH): Defined. + (TARGET_SCHED_DISPATCH_DO): Defined. + (DISPATCH_WINDOW_SIZE): Defined. + (MAX_DISPATCH_WINDOWS): Defined. + (MAX_INSN): Defined. + (MAX_IMM): Defined. + (MAX_IMM_SIZE): Defined. + (MAX_IMM_32): Defined. + (MAX_IMM_64): Defined. + (MAX_LOAD): Defined. + (MAX_STORE): Defined. + (BIG): Defined. + (dispatch_group): New. + (num_allowable_groups): New. + (group_name): New. + (sched_insn_info_s): New. + (dispatch_windows_s): New. + (imm_info_s): New. + (dispatch_window_list): New. + (dispatch_window_list1): New. + (get_mem_group): New. + (is_cmp): New. + (dispatch_violation): New. + (is_branch): New. + (is_prefetch): New. + (init_window): New. + (allocate_window): New. + (init_dispatch_sched): New. + (is_end_basic_block): New. + (process_end_window): New. + (allocate_next_window): New. + (find_constant_1): New. + (find_constant): New. + (get_num_immediates): New. + (has_immediate): New. + (get_insn_path): New. + (get_insn_group): New. + (count_num_restricted): New. + (fits_dispatch_window): New. + (add_insn_window): New. + (add_to_dispatch_window): New. + (debug_dispatch_window_file): New. + (debug_dispatch_window): New. + (debug_insn_dispatch_info_file): New. + (debug_ready_dispatch): New. + (do_dispatch): New. + (has_dispatch): New. + * config/i386/i386.h (debug_ready_dispatch): Declared. + (debug_dispatch_window): Declared. + * config/i386/i386.opt (mdispatch-scheduler): New flag. + * doc/tm.texi.in (TARGET_SCHED_DISPATCH): New. + (TARGET_SCHED_DISPATCH_DO): New. + * doc/tm.texi: Regererated. + * haifa-sched.c (choose_ready): Call targetm.sched.dispatch and + ready_remove_first_dispatch + (schedule_block): Call targetm.sched.dispatch and + targetm.sched.dispatch_do. + (sched_init): Call targetm.sched.dispatch and + targetm.sched.dispatch_do. + (ready_remove_first_dispatch): New. + (number_in_ready): New. + (get_ready_element): New. + * hooks.c (hook_bool_rtx_int_false): New. + (hook_void_rtx_int): New. + * hooks.h (hook_bool_rtx_int_false): Declared. + (hook_void_rtx_int): Declared. + * sched-int.h (IS_DISPATCH_ON): Defined. + (IS_CMP): Defined. + (DISPATCH_VIOLATION): Defined. + (FITS_DISPATCH_WINDOW): Defined. + (DISPATCH_INIT): Defined. + (ADD_TO_DISPATCH_WINDOW): Defined. + (get_ready_element): Declared. + (number_in_ready): Declared. + * target.def (dispatch): Defined. + (dispatch_do): Defined. + 2010-09-09 Vladimir Makarov <vmakarov@redhat.com> PR middle-end/45312 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index a3f0567ae4f..1d79a18b93a 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -55,7 +55,7 @@ along with GCC; see the file COPYING3. If not see #include "cselib.h" #include "debug.h" #include "dwarf2out.h" - +#include "sched-int.h" static rtx legitimize_dllimport_symbol (rtx, bool); #ifndef CHECK_STACK_LIMIT @@ -31534,6 +31534,806 @@ ix86_enum_va_list (int idx, const char **pname, tree *ptree) return 0; } +#undef TARGET_SCHED_DISPATCH +#define TARGET_SCHED_DISPATCH has_dispatch +#undef TARGET_SCHED_DISPATCH_DO +#define TARGET_SCHED_DISPATCH_DO do_dispatch + +/* The size of the dispatch window is the total number of bytes of + object code allowed in a window. */ +#define DISPATCH_WINDOW_SIZE 16 + +/* Number of dispatch windows considered for scheduling. */ +#define MAX_DISPATCH_WINDOWS 3 + +/* Maximum number of instructions in a window. */ +#define MAX_INSN 4 + +/* Maximum number of immediate operands in a window. */ +#define MAX_IMM 4 + +/* Maximum number of immediate bits allowed in a window. */ +#define MAX_IMM_SIZE 128 + +/* Maximum number of 32 bit immediates allowed in a window. */ +#define MAX_IMM_32 4 + +/* Maximum number of 64 bit immediates allowed in a window. */ +#define MAX_IMM_64 2 + +/* Maximum total of loads or prefetches allowed in a window. */ +#define MAX_LOAD 2 + +/* Maximum total of stores allowed in a window. */ +#define MAX_STORE 1 + +#undef BIG +#define BIG 100 + + +/* Dispatch groups. Istructions that affect the mix in a dispatch window. */ +enum dispatch_group { + disp_no_group = 0, + disp_load, + disp_store, + disp_load_store, + disp_prefetch, + disp_imm, + disp_imm_32, + disp_imm_64, + disp_branch, + disp_cmp, + disp_jcc, + disp_last +}; + +/* Number of allowable groups in a dispatch window. It is an array + indexed by dispatch_group enum. 100 is used as a big number, + because the number of these kind of operations does not have any + effect in dispatch window, but we need them for other reasons in + the table. */ +static unsigned int num_allowable_groups[disp_last] = { + 0, 2, 1, 1, 2, 4, 4, 2, 1, BIG, BIG +}; + +char group_name[disp_last + 1][16] = { + "disp_no_group", "disp_load", "disp_store", "disp_load_store", + "disp_prefetch", "disp_imm", "disp_imm_32", "disp_imm_64", + "disp_branch", "disp_cmp", "disp_jcc", "disp_last" +}; + +/* Instruction path. */ +enum insn_path { + no_path = 0, + path_single, /* Single micro op. */ + path_double, /* Double micro op. */ + path_multi, /* Instructions with more than 2 micro op.. */ + last_path +}; + +/* sched_insn_info defines a window to the instructions scheduled in + the basic block. It contains a pointer to the insn_info table and + the instruction scheduled. + + Windows are allocated for each basic block and are linked + together. */ +typedef struct sched_insn_info_s { + rtx insn; + enum dispatch_group group; + enum insn_path path; + int byte_len; + int imm_bytes; +} sched_insn_info; + +/* Linked list of dispatch windows. This is a two way list of + dispatch windows of a basic block. It contains information about + the number of uops in the window and the total number of + instructions and of bytes in the object code for this dispatch + window. */ +typedef struct dispatch_windows_s { + int num_insn; /* Number of insn in the window. */ + int num_uops; /* Number of uops in the window. */ + int window_size; /* Number of bytes in the window. */ + int window_num; /* Window number between 0 or 1. */ + int num_imm; /* Number of immediates in an insn. */ + int num_imm_32; /* Number of 32 bit immediates in an insn. */ + int num_imm_64; /* Number of 64 bit immediates in an insn. */ + int imm_size; /* Total immediates in the window. */ + int num_loads; /* Total memory loads in the window. */ + int num_stores; /* Total memory stores in the window. */ + int violation; /* Violation exists in window. */ + sched_insn_info *window; /* Pointer to the window. */ + struct dispatch_windows_s *next; + struct dispatch_windows_s *prev; +} dispatch_windows; + +/* Immediate valuse used in an insn. */ +typedef struct imm_info_s + { + int imm; + int imm32; + int imm64; + } imm_info; + +static dispatch_windows *dispatch_window_list; +static dispatch_windows *dispatch_window_list1; + +/* Get dispatch group of insn. */ + +static enum dispatch_group +get_mem_group (rtx insn) +{ + enum attr_memory memory; + + if (INSN_CODE (insn) < 0) + return disp_no_group; + memory = get_attr_memory (insn); + if (memory == MEMORY_STORE) + return disp_store; + + if (memory == MEMORY_LOAD) + return disp_load; + + if (memory == MEMORY_BOTH) + return disp_load_store; + + return disp_no_group; +} + +/* Return true if insn is a compare instruction. */ + +static bool +is_cmp (rtx insn) +{ + enum attr_type type; + + type = get_attr_type (insn); + return (type == TYPE_TEST + || type == TYPE_ICMP + || type == TYPE_FCMP + || GET_CODE (PATTERN (insn)) == COMPARE); +} + +/* Return true if a dispatch violation encountered. */ + +static bool +dispatch_violation (void) +{ + if (dispatch_window_list->next) + return dispatch_window_list->next->violation; + return dispatch_window_list->violation; +} + +/* Return true if insn is a branch instruction. */ + +static bool +is_branch (rtx insn) +{ + return (CALL_P (insn) || JUMP_P (insn)); +} + +/* Return true if insn is a prefetch instruction. */ + +static bool +is_prefetch (rtx insn) +{ + return NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == PREFETCH; +} + +/* This function initializes a dispatch window and the list container holding a + pointer to the window. */ + +static void +init_window (int window_num) +{ + int i; + dispatch_windows *new_list; + + if (window_num == 0) + new_list = dispatch_window_list; + else + new_list = dispatch_window_list1; + + new_list->num_insn = 0; + new_list->num_uops = 0; + new_list->window_size = 0; + new_list->next = NULL; + new_list->prev = NULL; + new_list->window_num = window_num; + new_list->num_imm = 0; + new_list->num_imm_32 = 0; + new_list->num_imm_64 = 0; + new_list->imm_size = 0; + new_list->num_loads = 0; + new_list->num_stores = 0; + new_list->violation = false; + + for (i = 0; i < MAX_INSN; i++) + { + new_list->window[i].insn = NULL; + new_list->window[i].group = disp_no_group; + new_list->window[i].path = no_path; + new_list->window[i].byte_len = 0; + new_list->window[i].imm_bytes = 0; + } + return; +} + +/* This function allocates and initializes a dispatch window and the + list container holding a pointer to the window. */ + +static dispatch_windows * +allocate_window (void) +{ + dispatch_windows *new_list = XNEW (struct dispatch_windows_s); + new_list->window = XNEWVEC (struct sched_insn_info_s, MAX_INSN + 1); + + return new_list; +} + +/* This routine initializes the dispatch scheduling information. It + initiates building dispatch scheduler tables and constructs the + first dispatch window. */ + +static void +init_dispatch_sched (void) +{ + /* Allocate a dispatch list and a window. */ + dispatch_window_list = allocate_window (); + dispatch_window_list1 = allocate_window (); + init_window (0); + init_window (1); +} + +/* This function returns true if a branch is detected. End of a basic block + does not have to be a branch, but here we assume only branches end a + window. */ + +static bool +is_end_basic_block (enum dispatch_group group) +{ + return group == disp_branch; +} + +/* This function is called when the end of a window processing is reached. */ + +static void +process_end_window (void) +{ + gcc_assert (dispatch_window_list->num_insn <= MAX_INSN); + if (dispatch_window_list->next) + { + gcc_assert (dispatch_window_list1->num_insn <= MAX_INSN); + gcc_assert (dispatch_window_list->window_size + + dispatch_window_list1->window_size <= 48); + init_window (1); + } + init_window (0); +} + +/* Allocates a new dispatch window and adds it to WINDOW_LIST. + WINDOW_NUM is either 0 or 1. A maximum of two windows are generated + for 48 bytes of instructions. Note that these windows are not dispatch + windows that their sizes are DISPATCH_WINDOW_SIZE. */ + +static dispatch_windows * +allocate_next_window (int window_num) +{ + if (window_num == 0) + { + if (dispatch_window_list->next) + init_window (1); + init_window (0); + return dispatch_window_list; + } + + dispatch_window_list->next = dispatch_window_list1; + dispatch_window_list1->prev = dispatch_window_list; + + return dispatch_window_list1; +} + +/* Increment the number of immediate operands of an instruction. */ + +static int +find_constant_1 (rtx *in_rtx, imm_info *imm_values) +{ + if (*in_rtx == 0) + return 0; + + switch ( GET_CODE (*in_rtx)) + { + case CONST: + case SYMBOL_REF: + case CONST_INT: + (imm_values->imm)++; + if (x86_64_immediate_operand (*in_rtx, SImode)) + (imm_values->imm32)++; + else + (imm_values->imm64)++; + break; + + case CONST_DOUBLE: + (imm_values->imm)++; + (imm_values->imm64)++; + break; + + case CODE_LABEL: + if (LABEL_KIND (*in_rtx) == LABEL_NORMAL) + { + (imm_values->imm)++; + (imm_values->imm32)++; + } + break; + + default: + break; + } + + return 0; +} + +/* Compute number of immediate operands of an instruction. */ + +static void +find_constant (rtx in_rtx, imm_info *imm_values) +{ + for_each_rtx (INSN_P (in_rtx) ? &PATTERN (in_rtx) : &in_rtx, + (rtx_function) find_constant_1, (void *) imm_values); +} + +/* Return total size of immediate operands of an instruction along with number + of corresponding immediate-operands. It initializes its parameters to zero + befor calling FIND_CONSTANT. + INSN is the input instruction. IMM is the total of immediates. + IMM32 is the number of 32 bit immediates. IMM64 is the number of 64 + bit immediates. */ + +static int +get_num_immediates (rtx insn, int *imm, int *imm32, int *imm64) +{ + imm_info imm_values = {0, 0, 0}; + + find_constant (insn, &imm_values); + *imm = imm_values.imm; + *imm32 = imm_values.imm32; + *imm64 = imm_values.imm64; + return imm_values.imm32 * 4 + imm_values.imm64 * 8; +} + +/* This function indicates if an operand of an instruction is an + immediate. */ + +static bool +has_immediate (rtx insn) +{ + int num_imm_operand; + int num_imm32_operand; + int num_imm64_operand; + + if (insn) + return get_num_immediates (insn, &num_imm_operand, &num_imm32_operand, + &num_imm64_operand); + return false; +} + +/* Return single or double path for instructions. */ + +static enum insn_path +get_insn_path (rtx insn) +{ + enum attr_amdfam10_decode path = get_attr_amdfam10_decode (insn); + + if ((int)path == 0) + return path_single; + + if ((int)path == 1) + return path_double; + + return path_multi; +} + +/* Return insn dispatch group. */ + +static enum dispatch_group +get_insn_group (rtx insn) +{ + enum dispatch_group group = get_mem_group (insn); + if (group) + return group; + + if (is_branch (insn)) + return disp_branch; + + if (is_cmp (insn)) + return disp_cmp; + + if (has_immediate (insn)) + return disp_imm; + + if (is_prefetch (insn)) + return disp_prefetch; + + return disp_no_group; +} + +/* Count number of GROUP restricted instructions in a dispatch + window WINDOW_LIST. */ + +static int +count_num_restricted (rtx insn, dispatch_windows *window_list) +{ + enum dispatch_group group = get_insn_group (insn); + int imm_size; + int num_imm_operand; + int num_imm32_operand; + int num_imm64_operand; + + if (group == disp_no_group) + return 0; + + if (group == disp_imm) + { + imm_size = get_num_immediates (insn, &num_imm_operand, &num_imm32_operand, + &num_imm64_operand); + if (window_list->imm_size + imm_size > MAX_IMM_SIZE + || num_imm_operand + window_list->num_imm > MAX_IMM + || (num_imm32_operand > 0 + && (window_list->num_imm_32 + num_imm32_operand > MAX_IMM_32 + || window_list->num_imm_64 * 2 + num_imm32_operand > MAX_IMM_32)) + || (num_imm64_operand > 0 + && (window_list->num_imm_64 + num_imm64_operand > MAX_IMM_64 + || window_list->num_imm_32 + num_imm64_operand * 2 > MAX_IMM_32)) + || (window_list->imm_size + imm_size == MAX_IMM_SIZE + && num_imm64_operand > 0 + && ((window_list->num_imm_64 > 0 + && window_list->num_insn >= 2) + || window_list->num_insn >= 3))) + return BIG; + + return 1; + } + + if ((group == disp_load_store + && (window_list->num_loads >= MAX_LOAD + || window_list->num_stores >= MAX_STORE)) + || ((group == disp_load + || group == disp_prefetch) + && window_list->num_loads >= MAX_LOAD) + || (group == disp_store + && window_list->num_stores >= MAX_STORE)) + return BIG; + + return 1; +} + +/* This function returns true if insn satisfies dispatch rules on the + last window scheduled. */ + +static bool +fits_dispatch_window (rtx insn) +{ + dispatch_windows *window_list = dispatch_window_list; + dispatch_windows *window_list_next = dispatch_window_list->next; + unsigned int num_restrict; + enum dispatch_group group = get_insn_group (insn); + enum insn_path path = get_insn_path (insn); + int sum; + + /* Make disp_cmp and disp_jcc get scheduled at the latest. These + instructions should be given the lowest priority in the + scheduling process in Haifa scheduler to make sure they will be + scheduled in the same dispatch window as the refrence to them. */ + if (group == disp_jcc || group == disp_cmp) + return false; + + /* Check nonrestricted. */ + if (group == disp_no_group || group == disp_branch) + return true; + + /* Get last dispatch window. */ + if (window_list_next) + window_list = window_list_next; + + if (window_list->window_num == 1) + { + sum = window_list->prev->window_size + window_list->window_size; + + if (sum == 32 + || (min_insn_size (insn) + sum) >= 48) + /* Window 1 is full. Go for next window. */ + return true; + } + + num_restrict = count_num_restricted (insn, window_list); + + if (num_restrict > num_allowable_groups[group]) + return false; + + /* See if it fits in the first window. */ + if (window_list->window_num == 0) + { + /* The first widow should have only single and double path + uops. */ + if (path == path_double + && (window_list->num_uops + 2) > MAX_INSN) + return false; + else if (path != path_single) + return false; + } + return true; +} + +/* Add an instruction INSN with NUM_UOPS micro-operations to the + dispatch window WINDOW_LIST. */ + +static void +add_insn_window (rtx insn, dispatch_windows *window_list, int num_uops) +{ + int byte_len = min_insn_size (insn); + int num_insn = window_list->num_insn; + int imm_size; + sched_insn_info *window = window_list->window; + enum dispatch_group group = get_insn_group (insn); + enum insn_path path = get_insn_path (insn); + int num_imm_operand; + int num_imm32_operand; + int num_imm64_operand; + + if (!window_list->violation && group != disp_cmp + && !fits_dispatch_window (insn)) + window_list->violation = true; + + imm_size = get_num_immediates (insn, &num_imm_operand, &num_imm32_operand, + &num_imm64_operand); + + /* Initialize window with new instruction. */ + window[num_insn].insn = insn; + window[num_insn].byte_len = byte_len; + window[num_insn].group = group; + window[num_insn].path = path; + window[num_insn].imm_bytes = imm_size; + + window_list->window_size += byte_len; + window_list->num_insn = num_insn + 1; + window_list->num_uops = window_list->num_uops + num_uops; + window_list->imm_size += imm_size; + window_list->num_imm += num_imm_operand; + window_list->num_imm_32 += num_imm32_operand; + window_list->num_imm_64 += num_imm64_operand; + + if (group == disp_store) + window_list->num_stores += 1; + else if (group == disp_load + || group == disp_prefetch) + window_list->num_loads += 1; + else if (group == disp_load_store) + { + window_list->num_stores += 1; + window_list->num_loads += 1; + } +} + +/* Adds a scheduled instruction, INSN, to the current dispatch window. + If the total bytes of instructions or the number of instructions in + the window exceed allowable, it allocates a new window. */ + +static void +add_to_dispatch_window (rtx insn) +{ + int byte_len; + dispatch_windows *window_list; + dispatch_windows *next_list; + dispatch_windows *window0_list; + enum insn_path path; + enum dispatch_group insn_group; + bool insn_fits; + int num_insn; + int num_uops; + int window_num; + int insn_num_uops; + int sum; + + if (INSN_CODE (insn) < 0) + return; + + byte_len = min_insn_size (insn); + window_list = dispatch_window_list; + next_list = window_list->next; + path = get_insn_path (insn); + insn_group = get_insn_group (insn); + + /* Get the last dispatch window. */ + if (next_list) + window_list = dispatch_window_list->next; + + if (path == path_single) + insn_num_uops = 1; + else if (path == path_double) + insn_num_uops = 2; + else + insn_num_uops = (int) path; + + /* If current window is full, get a new window. + Window number zero is full, if MAX_INSN uops are scheduled in it. + Window number one is full, if window zero's bytes plus window + one's bytes is 32, or if the bytes of the new instruction added + to the total makes it greater than 48, or it has already MAX_INSN + instructions in it. */ + num_insn = window_list->num_insn; + num_uops = window_list->num_uops; + window_num = window_list->window_num; + insn_fits = fits_dispatch_window (insn); + + if (num_insn >= MAX_INSN + || num_uops + insn_num_uops > MAX_INSN + || !(insn_fits)) + { + window_num = ~window_num & 1; + window_list = allocate_next_window (window_num); + } + + if (window_num == 0) + { + add_insn_window (insn, window_list, insn_num_uops); + if (window_list->num_insn >= MAX_INSN + && insn_group == disp_branch) + { + process_end_window (); + return; + } + } + else if (window_num == 1) + { + window0_list = window_list->prev; + sum = window0_list->window_size + window_list->window_size; + if (sum == 32 + || (byte_len + sum) >= 48) + { + process_end_window (); + window_list = dispatch_window_list; + } + + add_insn_window (insn, window_list, insn_num_uops); + } + else + gcc_unreachable (); + + if (is_end_basic_block (insn_group)) + { + /* End of basic block is reached do end-basic-block process. */ + process_end_window (); + return; + } +} + +/* Print the dispatch window, WINDOW_NUM, to FILE. */ + +DEBUG_FUNCTION static void +debug_dispatch_window_file (FILE *file, int window_num) +{ + dispatch_windows *list; + int i; + + if (window_num == 0) + list = dispatch_window_list; + else + list = dispatch_window_list1; + + fprintf (file, "Window #%d:\n", list->window_num); + fprintf (file, " num_insn = %d, num_uops = %d, window_size = %d\n", + list->num_insn, list->num_uops, list->window_size); + fprintf (file, " num_imm = %d, num_imm_32 = %d, num_imm_64 = %d, imm_size = %d\n", + list->num_imm, list->num_imm_32, list->num_imm_64, list->imm_size); + + fprintf (file, " num_loads = %d, num_stores = %d\n", list->num_loads, + list->num_stores); + fprintf (file, " insn info:\n"); + + for (i = 0; i < MAX_INSN; i++) + { + if (!list->window[i].insn) + break; + fprintf (file, " group[%d] = %s, insn[%d] = %p, path[%d] = %d byte_len[%d] = %d, imm_bytes[%d] = %d\n", + i, group_name[list->window[i].group], + i, (void *)list->window[i].insn, + i, list->window[i].path, + i, list->window[i].byte_len, + i, list->window[i].imm_bytes); + } +} + +/* Print to stdout a dispatch window. */ + +DEBUG_FUNCTION void +debug_dispatch_window (int window_num) +{ + debug_dispatch_window_file (stdout, window_num); +} + +/* Print INSN dispatch information to FILE. */ + +DEBUG_FUNCTION static void +debug_insn_dispatch_info_file (FILE *file, rtx insn) +{ + int byte_len; + enum insn_path path; + enum dispatch_group group; + int imm_size; + int num_imm_operand; + int num_imm32_operand; + int num_imm64_operand; + + if (INSN_CODE (insn) < 0) + return; + + byte_len = min_insn_size (insn); + path = get_insn_path (insn); + group = get_insn_group (insn); + imm_size = get_num_immediates (insn, &num_imm_operand, &num_imm32_operand, + &num_imm64_operand); + + fprintf (file, " insn info:\n"); + fprintf (file, " group = %s, path = %d, byte_len = %d\n", + group_name[group], path, byte_len); + fprintf (file, " num_imm = %d, num_imm_32 = %d, num_imm_64 = %d, imm_size = %d\n", + num_imm_operand, num_imm32_operand, num_imm64_operand, imm_size); +} + +/* Print to STDERR the status of the ready list with respect to + dispatch windows. */ + +DEBUG_FUNCTION void +debug_ready_dispatch (void) +{ + int i; + int no_ready = number_in_ready (); + + fprintf (stdout, "Number of ready: %d\n", no_ready); + + for (i = 0; i < no_ready; i++) + debug_insn_dispatch_info_file (stdout, get_ready_element (i)); +} + +/* This routine is the driver of the dispatch scheduler. */ + +static void +do_dispatch (rtx insn, int mode) +{ + if (mode == DISPATCH_INIT) + init_dispatch_sched (); + else if (mode == ADD_TO_DISPATCH_WINDOW) + add_to_dispatch_window (insn); +} + +/* Return TRUE if Dispatch Scheduling is supported. */ + +static bool +has_dispatch (rtx insn, int action) +{ + if (ix86_tune == PROCESSOR_BDVER1 && flag_dispatch_scheduler) + switch (action) + { + default: + return false; + + case IS_DISPATCH_ON: + return true; + break; + + case IS_CMP: + return is_cmp (insn); + + case DISPATCH_VIOLATION: + return dispatch_violation (); + + case FITS_DISPATCH_WINDOW: + return fits_dispatch_window (insn); + } + + return false; +} + /* Initialize the GCC target structure. */ #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 49402d185b1..91238d5d7c7 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2375,6 +2375,9 @@ struct GTY(()) machine_function { #define SYMBOL_REF_DLLEXPORT_P(X) \ ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_DLLEXPORT) != 0) +extern void debug_ready_dispatch (void); +extern void debug_dispatch_window (int); + /* Local variables: version-control: t diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index 8009830ebfe..5790e767511 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -254,6 +254,11 @@ Enable automatic generation of fused floating point multiply-add instructions if the ISA supports such instructions. The -mfused-madd option is on by default. +mdispatch-scheduler +Target RejectNegative Var(flag_dispatch_scheduler) +Do dispatch scheduling if processor is bdver1 and Haifa scheduling +is selected. + ;; ISA support m32 diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index e60392f9fa8..91fbaf7a893 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -6777,6 +6777,16 @@ bound will be used in case this hook is not implemented: the total number of instructions divided by the issue rate. @end deftypefn +@deftypefn {Target Hook} bool TARGET_SCHED_DISPATCH (rtx @var{insn}, int @var{x}) +This hook is called by Haifa Scheduler. It returns true if dispatch scheduling +is supported in hardware and the condition specified in the parameter is true. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_SCHED_DISPATCH_DO (rtx @var{insn}, int @var{x}) +This hook is called by Haifa Scheduler. It performs the operation specified +in its second parameter. +@end deftypefn + @node Sections @section Dividing the Output into Sections (Texts, Data, @dots{}) @c the above section title is WAY too long. maybe cut the part between diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 17fcd4bd4de..a180a56c9ab 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -6777,6 +6777,16 @@ bound will be used in case this hook is not implemented: the total number of instructions divided by the issue rate. @end deftypefn +@hook TARGET_SCHED_DISPATCH +This hook is called by Haifa Scheduler. It returns true if dispatch scheduling +is supported in hardware and the condition specified in the parameter is true. +@end deftypefn + +@hook TARGET_SCHED_DISPATCH_DO +This hook is called by Haifa Scheduler. It performs the operation specified +in its second parameter. +@end deftypefn + @node Sections @section Dividing the Output into Sections (Texts, Data, @dots{}) @c the above section title is WAY too long. maybe cut the part between diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index 278768547a8..5b5459f7cae 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -532,6 +532,7 @@ static void extend_h_i_d (void); static void ready_add (struct ready_list *, rtx, bool); static rtx ready_remove_first (struct ready_list *); +static rtx ready_remove_first_dispatch (struct ready_list *ready); static void queue_to_ready (struct ready_list *); static int early_queue_to_ready (state_t, struct ready_list *); @@ -2642,7 +2643,11 @@ choose_ready (struct ready_list *ready, rtx *insn_ptr) if (lookahead <= 0 || SCHED_GROUP_P (ready_element (ready, 0)) || DEBUG_INSN_P (ready_element (ready, 0))) { - *insn_ptr = ready_remove_first (ready); + if (targetm.sched.dispatch (NULL_RTX, IS_DISPATCH_ON)) + *insn_ptr = ready_remove_first_dispatch (ready); + else + *insn_ptr = ready_remove_first (ready); + return 0; } else @@ -3140,6 +3145,10 @@ schedule_block (basic_block *target_bb) last_scheduled_insn); move_insn (insn, last_scheduled_insn, current_sched_info->next_tail); + + if (targetm.sched.dispatch (NULL_RTX, IS_DISPATCH_ON)) + targetm.sched.dispatch_do (insn, ADD_TO_DISPATCH_WINDOW); + reemit_notes (insn); last_scheduled_insn = insn; @@ -3364,8 +3373,12 @@ sched_init (void) flag_schedule_speculative_load = 0; #endif + if (targetm.sched.dispatch (NULL_RTX, IS_DISPATCH_ON)) + targetm.sched.dispatch_do (NULL_RTX, DISPATCH_INIT); + sched_pressure_p = (flag_sched_pressure && ! reload_completed && common_sched_info->sched_pass_id == SCHED_RGN_PASS); + if (sched_pressure_p) ira_setup_eliminable_regset (); @@ -5557,4 +5570,73 @@ sched_emit_insn (rtx pat) return insn; } +/* This function returns a candidate satisfying dispatch constraints from + the ready list. */ + +static rtx +ready_remove_first_dispatch (struct ready_list *ready) +{ + int i; + rtx insn = ready_element (ready, 0); + + if (ready->n_ready == 1 + || INSN_CODE (insn) < 0 + || !INSN_P (insn) + || !active_insn_p (insn) + || targetm.sched.dispatch (insn, FITS_DISPATCH_WINDOW)) + return ready_remove_first (ready); + + for (i = 1; i < ready->n_ready; i++) + { + insn = ready_element (ready, i); + + if (INSN_CODE (insn) < 0 + || !INSN_P (insn) + || !active_insn_p (insn)) + continue; + + if (targetm.sched.dispatch (insn, FITS_DISPATCH_WINDOW)) + { + /* Return ith element of ready. */ + insn = ready_remove (ready, i); + return insn; + } + } + + if (targetm.sched.dispatch (NULL_RTX, DISPATCH_VIOLATION)) + return ready_remove_first (ready); + + for (i = 1; i < ready->n_ready; i++) + { + insn = ready_element (ready, i); + + if (INSN_CODE (insn) < 0 + || !INSN_P (insn) + || !active_insn_p (insn)) + continue; + + /* Return i-th element of ready. */ + if (targetm.sched.dispatch (insn, IS_CMP)) + return ready_remove (ready, i); + } + + return ready_remove_first (ready); +} + +/* Get number of ready insn in the ready list. */ + +int +number_in_ready (void) +{ + return ready.n_ready; +} + +/* Get number of ready's in the ready list. */ + +rtx +get_ready_element (int i) +{ + return ready_element (&ready, i); +} + #endif /* INSN_SCHEDULING */ diff --git a/gcc/hooks.c b/gcc/hooks.c index 52a40484054..4d5be73c599 100644 --- a/gcc/hooks.c +++ b/gcc/hooks.c @@ -340,3 +340,18 @@ hook_tree_const_tree_null (const_tree t ATTRIBUTE_UNUSED) { return NULL; } + +/* Generic hook that takes a rtx and an int and returns a bool. */ + +bool +hook_bool_rtx_int_false (rtx insn ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED) +{ + return false; +} + +/* Generic hook that takes a rtx and an int and returns void. */ + +void +hook_void_rtx_int (rtx insn ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED) +{ +} diff --git a/gcc/hooks.h b/gcc/hooks.h index a89a355f879..9e4a0aeeb74 100644 --- a/gcc/hooks.h +++ b/gcc/hooks.h @@ -46,6 +46,7 @@ extern bool hook_bool_const_tree_hwi_hwi_const_tree_true (const_tree, HOST_WIDE_INT, const_tree); extern bool hook_bool_rtx_false (rtx); +extern bool hook_bool_rtx_int_false (rtx, int); extern bool hook_bool_uintp_uintp_false (unsigned int *, unsigned int *); extern bool hook_bool_rtx_int_int_intp_bool_false (rtx, int, int, int *, bool); extern bool hook_bool_size_t_constcharptr_int_true (size_t, const char *, int); @@ -55,6 +56,7 @@ extern bool hook_bool_tree_bool_false (tree, bool); extern void hook_void_void (void); extern void hook_void_constcharptr (const char *); +extern void hook_void_rtx_int (rtx, int); extern void hook_void_FILEptr_constcharptr (FILE *, const char *); extern void hook_void_tree (tree); extern void hook_void_tree_treeptr (tree, tree *); diff --git a/gcc/sched-int.h b/gcc/sched-int.h index 147e264848a..fd2e15d70cf 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -1269,6 +1269,8 @@ extern void add_block (basic_block, basic_block); extern rtx bb_note (basic_block); extern void concat_note_lists (rtx, rtx *); extern rtx sched_emit_insn (rtx); +extern rtx get_ready_element (int); +extern int number_in_ready (void); /* Types and functions in sched-rgn.c. */ @@ -1477,6 +1479,13 @@ sd_iterator_next (sd_iterator_def *it_ptr) sd_iterator_cond (&(ITER), &(DEP)); \ sd_iterator_next (&(ITER))) +#define IS_DISPATCH_ON 1 +#define IS_CMP 2 +#define DISPATCH_VIOLATION 3 +#define FITS_DISPATCH_WINDOW 4 +#define DISPATCH_INIT 5 +#define ADD_TO_DISPATCH_WINDOW 6 + extern int sd_lists_size (const_rtx, sd_list_types_def); extern bool sd_lists_empty_p (const_rtx, sd_list_types_def); extern void sd_init_insn (rtx); diff --git a/gcc/target.def b/gcc/target.def index 6910ce9e232..cecf882c905 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -777,6 +777,24 @@ DEFHOOK "", int, (struct ddg *g), NULL) +/* The following member value is a function that initializes dispatch + schedling and adds instructions to dispatch window according to its + parameters. */ +DEFHOOK +(dispatch_do, +"", +void, (rtx insn, int x), +hook_void_rtx_int) + +/* The following member value is a a function that returns true is + dispatch schedling is supported in hardware and condition passed + as the second parameter is true. */ +DEFHOOK +(dispatch, +"", +bool, (rtx insn, int x), +hook_bool_rtx_int_false) + HOOK_VECTOR_END (sched) /* Functions relating to vectorization. */ |