1 | /* Scheduler hooks for IA-32 which implement bdver1-4 specific logic. |
2 | Copyright (C) 1988-2024 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by |
8 | the Free Software Foundation; either version 3, or (at your option) |
9 | any later version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #define IN_TARGET_CODE 1 |
21 | |
22 | #include "config.h" |
23 | #include "system.h" |
24 | #include "coretypes.h" |
25 | #include "backend.h" |
26 | #include "rtl.h" |
27 | #include "tree.h" |
28 | #include "cfghooks.h" |
29 | #include "tm_p.h" |
30 | #include "insn-config.h" |
31 | #include "insn-attr.h" |
32 | #include "recog.h" |
33 | #include "target.h" |
34 | #include "rtl-iter.h" |
35 | #include "regset.h" |
36 | #include "sched-int.h" |
37 | |
38 | /* The size of the dispatch window is the total number of bytes of |
39 | object code allowed in a window. */ |
40 | #define DISPATCH_WINDOW_SIZE 16 |
41 | |
42 | /* Number of dispatch windows considered for scheduling. */ |
43 | #define MAX_DISPATCH_WINDOWS 3 |
44 | |
45 | /* Maximum number of instructions in a window. */ |
46 | #define MAX_INSN 4 |
47 | |
48 | /* Maximum number of immediate operands in a window. */ |
49 | #define MAX_IMM 4 |
50 | |
51 | /* Maximum number of immediate bits allowed in a window. */ |
52 | #define MAX_IMM_SIZE 128 |
53 | |
54 | /* Maximum number of 32 bit immediates allowed in a window. */ |
55 | #define MAX_IMM_32 4 |
56 | |
57 | /* Maximum number of 64 bit immediates allowed in a window. */ |
58 | #define MAX_IMM_64 2 |
59 | |
60 | /* Maximum total of loads or prefetches allowed in a window. */ |
61 | #define MAX_LOAD 2 |
62 | |
63 | /* Maximum total of stores allowed in a window. */ |
64 | #define MAX_STORE 1 |
65 | |
66 | #undef BIG |
67 | #define BIG 100 |
68 | |
69 | |
70 | /* Dispatch groups. Instructions that affect the mix in a dispatch window. */ |
71 | enum dispatch_group { |
72 | disp_no_group = 0, |
73 | disp_load, |
74 | disp_store, |
75 | disp_load_store, |
76 | disp_prefetch, |
77 | disp_imm, |
78 | disp_imm_32, |
79 | disp_imm_64, |
80 | disp_branch, |
81 | disp_cmp, |
82 | disp_jcc, |
83 | disp_last |
84 | }; |
85 | |
86 | /* Number of allowable groups in a dispatch window. It is an array |
87 | indexed by dispatch_group enum. 100 is used as a big number, |
88 | because the number of these kind of operations does not have any |
89 | effect in dispatch window, but we need them for other reasons in |
90 | the table. */ |
91 | static unsigned int num_allowable_groups[disp_last] = { |
92 | 0, 2, 1, 1, 2, 4, 4, 2, 1, BIG, BIG |
93 | }; |
94 | |
95 | char group_name[disp_last + 1][16] = { |
96 | "disp_no_group" , "disp_load" , "disp_store" , "disp_load_store" , |
97 | "disp_prefetch" , "disp_imm" , "disp_imm_32" , "disp_imm_64" , |
98 | "disp_branch" , "disp_cmp" , "disp_jcc" , "disp_last" |
99 | }; |
100 | |
101 | /* Instruction path. */ |
102 | enum insn_path { |
103 | no_path = 0, |
104 | path_single, /* Single micro op. */ |
105 | path_double, /* Double micro op. */ |
106 | path_multi, /* Instructions with more than 2 micro op.. */ |
107 | last_path |
108 | }; |
109 | |
110 | /* sched_insn_info defines a window to the instructions scheduled in |
111 | the basic block. It contains a pointer to the insn_info table and |
112 | the instruction scheduled. |
113 | |
114 | Windows are allocated for each basic block and are linked |
115 | together. */ |
116 | typedef struct sched_insn_info_s { |
117 | rtx insn; |
118 | enum dispatch_group group; |
119 | enum insn_path path; |
120 | int byte_len; |
121 | int imm_bytes; |
122 | } sched_insn_info; |
123 | |
124 | /* Linked list of dispatch windows. This is a two way list of |
125 | dispatch windows of a basic block. It contains information about |
126 | the number of uops in the window and the total number of |
127 | instructions and of bytes in the object code for this dispatch |
128 | window. */ |
129 | typedef struct dispatch_windows_s { |
130 | int num_insn; /* Number of insn in the window. */ |
131 | int num_uops; /* Number of uops in the window. */ |
132 | int window_size; /* Number of bytes in the window. */ |
133 | int window_num; /* Window number between 0 or 1. */ |
134 | int num_imm; /* Number of immediates in an insn. */ |
135 | int num_imm_32; /* Number of 32 bit immediates in an insn. */ |
136 | int num_imm_64; /* Number of 64 bit immediates in an insn. */ |
137 | int imm_size; /* Total immediates in the window. */ |
138 | int num_loads; /* Total memory loads in the window. */ |
139 | int num_stores; /* Total memory stores in the window. */ |
140 | int violation; /* Violation exists in window. */ |
141 | sched_insn_info *window; /* Pointer to the window. */ |
142 | struct dispatch_windows_s *next; |
143 | struct dispatch_windows_s *prev; |
144 | } dispatch_windows; |
145 | |
146 | /* Immediate valuse used in an insn. */ |
147 | typedef struct imm_info_s |
148 | { |
149 | int imm; |
150 | int imm32; |
151 | int imm64; |
152 | } imm_info; |
153 | |
154 | static dispatch_windows *dispatch_window_list; |
155 | static dispatch_windows *dispatch_window_list1; |
156 | |
157 | /* Get dispatch group of insn. */ |
158 | |
159 | static enum dispatch_group |
160 | get_mem_group (rtx_insn *insn) |
161 | { |
162 | enum attr_memory memory; |
163 | |
164 | if (INSN_CODE (insn) < 0) |
165 | return disp_no_group; |
166 | memory = get_attr_memory (insn); |
167 | if (memory == MEMORY_STORE) |
168 | return disp_store; |
169 | |
170 | if (memory == MEMORY_LOAD) |
171 | return disp_load; |
172 | |
173 | if (memory == MEMORY_BOTH) |
174 | return disp_load_store; |
175 | |
176 | return disp_no_group; |
177 | } |
178 | |
179 | /* Return true if insn is a compare instruction. */ |
180 | |
181 | static bool |
182 | is_cmp (rtx_insn *insn) |
183 | { |
184 | enum attr_type type; |
185 | |
186 | type = get_attr_type (insn); |
187 | return (type == TYPE_TEST |
188 | || type == TYPE_ICMP |
189 | || type == TYPE_FCMP |
190 | || GET_CODE (PATTERN (insn)) == COMPARE); |
191 | } |
192 | |
193 | /* Return true if a dispatch violation encountered. */ |
194 | |
195 | static bool |
196 | dispatch_violation (void) |
197 | { |
198 | if (dispatch_window_list->next) |
199 | return dispatch_window_list->next->violation; |
200 | return dispatch_window_list->violation; |
201 | } |
202 | |
203 | /* Return true if insn is a branch instruction. */ |
204 | |
205 | static bool |
206 | is_branch (rtx_insn *insn) |
207 | { |
208 | return (CALL_P (insn) || JUMP_P (insn)); |
209 | } |
210 | |
211 | /* Return true if insn is a prefetch instruction. */ |
212 | |
213 | static bool |
214 | is_prefetch (rtx_insn *insn) |
215 | { |
216 | return NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == PREFETCH; |
217 | } |
218 | |
219 | /* This function initializes a dispatch window and the list container holding a |
220 | pointer to the window. */ |
221 | |
222 | static void |
223 | init_window (int window_num) |
224 | { |
225 | int i; |
226 | dispatch_windows *new_list; |
227 | |
228 | if (window_num == 0) |
229 | new_list = dispatch_window_list; |
230 | else |
231 | new_list = dispatch_window_list1; |
232 | |
233 | new_list->num_insn = 0; |
234 | new_list->num_uops = 0; |
235 | new_list->window_size = 0; |
236 | new_list->next = NULL; |
237 | new_list->prev = NULL; |
238 | new_list->window_num = window_num; |
239 | new_list->num_imm = 0; |
240 | new_list->num_imm_32 = 0; |
241 | new_list->num_imm_64 = 0; |
242 | new_list->imm_size = 0; |
243 | new_list->num_loads = 0; |
244 | new_list->num_stores = 0; |
245 | new_list->violation = false; |
246 | |
247 | for (i = 0; i < MAX_INSN; i++) |
248 | { |
249 | new_list->window[i].insn = NULL; |
250 | new_list->window[i].group = disp_no_group; |
251 | new_list->window[i].path = no_path; |
252 | new_list->window[i].byte_len = 0; |
253 | new_list->window[i].imm_bytes = 0; |
254 | } |
255 | return; |
256 | } |
257 | |
258 | /* This function allocates and initializes a dispatch window and the |
259 | list container holding a pointer to the window. */ |
260 | |
261 | static dispatch_windows * |
262 | allocate_window (void) |
263 | { |
264 | dispatch_windows *new_list = XNEW (struct dispatch_windows_s); |
265 | new_list->window = XNEWVEC (struct sched_insn_info_s, MAX_INSN + 1); |
266 | |
267 | return new_list; |
268 | } |
269 | |
270 | /* This routine initializes the dispatch scheduling information. It |
271 | initiates building dispatch scheduler tables and constructs the |
272 | first dispatch window. */ |
273 | |
274 | static void |
275 | init_dispatch_sched (void) |
276 | { |
277 | /* Allocate a dispatch list and a window. */ |
278 | dispatch_window_list = allocate_window (); |
279 | dispatch_window_list1 = allocate_window (); |
280 | init_window (window_num: 0); |
281 | init_window (window_num: 1); |
282 | } |
283 | |
284 | /* This function returns true if a branch is detected. End of a basic block |
285 | does not have to be a branch, but here we assume only branches end a |
286 | window. */ |
287 | |
288 | static bool |
289 | is_end_basic_block (enum dispatch_group group) |
290 | { |
291 | return group == disp_branch; |
292 | } |
293 | |
294 | /* This function is called when the end of a window processing is reached. */ |
295 | |
296 | static void |
297 | process_end_window (void) |
298 | { |
299 | gcc_assert (dispatch_window_list->num_insn <= MAX_INSN); |
300 | if (dispatch_window_list->next) |
301 | { |
302 | gcc_assert (dispatch_window_list1->num_insn <= MAX_INSN); |
303 | gcc_assert (dispatch_window_list->window_size |
304 | + dispatch_window_list1->window_size <= 48); |
305 | init_window (window_num: 1); |
306 | } |
307 | init_window (window_num: 0); |
308 | } |
309 | |
310 | /* Allocates a new dispatch window and adds it to WINDOW_LIST. |
311 | WINDOW_NUM is either 0 or 1. A maximum of two windows are generated |
312 | for 48 bytes of instructions. Note that these windows are not dispatch |
313 | windows that their sizes are DISPATCH_WINDOW_SIZE. */ |
314 | |
315 | static dispatch_windows * |
316 | allocate_next_window (int window_num) |
317 | { |
318 | if (window_num == 0) |
319 | { |
320 | if (dispatch_window_list->next) |
321 | init_window (window_num: 1); |
322 | init_window (window_num: 0); |
323 | return dispatch_window_list; |
324 | } |
325 | |
326 | dispatch_window_list->next = dispatch_window_list1; |
327 | dispatch_window_list1->prev = dispatch_window_list; |
328 | |
329 | return dispatch_window_list1; |
330 | } |
331 | |
332 | /* Compute number of immediate operands of an instruction. */ |
333 | |
334 | static void |
335 | find_constant (rtx in_rtx, imm_info *imm_values) |
336 | { |
337 | if (INSN_P (in_rtx)) |
338 | in_rtx = PATTERN (insn: in_rtx); |
339 | subrtx_iterator::array_type array; |
340 | FOR_EACH_SUBRTX (iter, array, in_rtx, ALL) |
341 | if (const_rtx x = *iter) |
342 | switch (GET_CODE (x)) |
343 | { |
344 | case CONST: |
345 | case SYMBOL_REF: |
346 | case CONST_INT: |
347 | (imm_values->imm)++; |
348 | if (x86_64_immediate_operand (CONST_CAST_RTX (x), SImode)) |
349 | (imm_values->imm32)++; |
350 | else |
351 | (imm_values->imm64)++; |
352 | break; |
353 | |
354 | case CONST_DOUBLE: |
355 | case CONST_WIDE_INT: |
356 | (imm_values->imm)++; |
357 | (imm_values->imm64)++; |
358 | break; |
359 | |
360 | case CODE_LABEL: |
361 | if (LABEL_KIND (x) == LABEL_NORMAL) |
362 | { |
363 | (imm_values->imm)++; |
364 | (imm_values->imm32)++; |
365 | } |
366 | break; |
367 | |
368 | default: |
369 | break; |
370 | } |
371 | } |
372 | |
373 | /* Return total size of immediate operands of an instruction along with number |
374 | of corresponding immediate-operands. It initializes its parameters to zero |
375 | befor calling FIND_CONSTANT. |
376 | INSN is the input instruction. IMM is the total of immediates. |
377 | IMM32 is the number of 32 bit immediates. IMM64 is the number of 64 |
378 | bit immediates. */ |
379 | |
380 | static int |
381 | get_num_immediates (rtx_insn *insn, int *imm, int *imm32, int *imm64) |
382 | { |
383 | imm_info imm_values = {.imm: 0, .imm32: 0, .imm64: 0}; |
384 | |
385 | find_constant (in_rtx: insn, imm_values: &imm_values); |
386 | *imm = imm_values.imm; |
387 | *imm32 = imm_values.imm32; |
388 | *imm64 = imm_values.imm64; |
389 | return imm_values.imm32 * 4 + imm_values.imm64 * 8; |
390 | } |
391 | |
392 | /* This function indicates if an operand of an instruction is an |
393 | immediate. */ |
394 | |
395 | static bool |
396 | has_immediate (rtx_insn *insn) |
397 | { |
398 | int num_imm_operand; |
399 | int num_imm32_operand; |
400 | int num_imm64_operand; |
401 | |
402 | if (insn) |
403 | return get_num_immediates (insn, imm: &num_imm_operand, imm32: &num_imm32_operand, |
404 | imm64: &num_imm64_operand); |
405 | return false; |
406 | } |
407 | |
408 | /* Return single or double path for instructions. */ |
409 | |
410 | static enum insn_path |
411 | get_insn_path (rtx_insn *insn) |
412 | { |
413 | enum attr_amdfam10_decode path = get_attr_amdfam10_decode (insn); |
414 | |
415 | if ((int)path == 0) |
416 | return path_single; |
417 | |
418 | if ((int)path == 1) |
419 | return path_double; |
420 | |
421 | return path_multi; |
422 | } |
423 | |
424 | /* Return insn dispatch group. */ |
425 | |
426 | static enum dispatch_group |
427 | get_insn_group (rtx_insn *insn) |
428 | { |
429 | enum dispatch_group group = get_mem_group (insn); |
430 | if (group) |
431 | return group; |
432 | |
433 | if (is_branch (insn)) |
434 | return disp_branch; |
435 | |
436 | if (is_cmp (insn)) |
437 | return disp_cmp; |
438 | |
439 | if (has_immediate (insn)) |
440 | return disp_imm; |
441 | |
442 | if (is_prefetch (insn)) |
443 | return disp_prefetch; |
444 | |
445 | return disp_no_group; |
446 | } |
447 | |
448 | /* Count number of GROUP restricted instructions in a dispatch |
449 | window WINDOW_LIST. */ |
450 | |
451 | static int |
452 | count_num_restricted (rtx_insn *insn, dispatch_windows *window_list) |
453 | { |
454 | enum dispatch_group group = get_insn_group (insn); |
455 | int imm_size; |
456 | int num_imm_operand; |
457 | int num_imm32_operand; |
458 | int num_imm64_operand; |
459 | |
460 | if (group == disp_no_group) |
461 | return 0; |
462 | |
463 | if (group == disp_imm) |
464 | { |
465 | imm_size = get_num_immediates (insn, imm: &num_imm_operand, imm32: &num_imm32_operand, |
466 | imm64: &num_imm64_operand); |
467 | if (window_list->imm_size + imm_size > MAX_IMM_SIZE |
468 | || num_imm_operand + window_list->num_imm > MAX_IMM |
469 | || (num_imm32_operand > 0 |
470 | && (window_list->num_imm_32 + num_imm32_operand > MAX_IMM_32 |
471 | || window_list->num_imm_64 * 2 + num_imm32_operand > MAX_IMM_32)) |
472 | || (num_imm64_operand > 0 |
473 | && (window_list->num_imm_64 + num_imm64_operand > MAX_IMM_64 |
474 | || window_list->num_imm_32 + num_imm64_operand * 2 > MAX_IMM_32)) |
475 | || (window_list->imm_size + imm_size == MAX_IMM_SIZE |
476 | && num_imm64_operand > 0 |
477 | && ((window_list->num_imm_64 > 0 |
478 | && window_list->num_insn >= 2) |
479 | || window_list->num_insn >= 3))) |
480 | return BIG; |
481 | |
482 | return 1; |
483 | } |
484 | |
485 | if ((group == disp_load_store |
486 | && (window_list->num_loads >= MAX_LOAD |
487 | || window_list->num_stores >= MAX_STORE)) |
488 | || ((group == disp_load |
489 | || group == disp_prefetch) |
490 | && window_list->num_loads >= MAX_LOAD) |
491 | || (group == disp_store |
492 | && window_list->num_stores >= MAX_STORE)) |
493 | return BIG; |
494 | |
495 | return 1; |
496 | } |
497 | |
498 | /* This function returns true if insn satisfies dispatch rules on the |
499 | last window scheduled. */ |
500 | |
501 | static bool |
502 | fits_dispatch_window (rtx_insn *insn) |
503 | { |
504 | dispatch_windows *window_list = dispatch_window_list; |
505 | dispatch_windows *window_list_next = dispatch_window_list->next; |
506 | unsigned int num_restrict; |
507 | enum dispatch_group group = get_insn_group (insn); |
508 | enum insn_path path = get_insn_path (insn); |
509 | int sum; |
510 | |
511 | /* Make disp_cmp and disp_jcc get scheduled at the latest. These |
512 | instructions should be given the lowest priority in the |
513 | scheduling process in Haifa scheduler to make sure they will be |
514 | scheduled in the same dispatch window as the reference to them. */ |
515 | if (group == disp_jcc || group == disp_cmp) |
516 | return false; |
517 | |
518 | /* Check nonrestricted. */ |
519 | if (group == disp_no_group || group == disp_branch) |
520 | return true; |
521 | |
522 | /* Get last dispatch window. */ |
523 | if (window_list_next) |
524 | window_list = window_list_next; |
525 | |
526 | if (window_list->window_num == 1) |
527 | { |
528 | sum = window_list->prev->window_size + window_list->window_size; |
529 | |
530 | if (sum == 32 |
531 | || (ix86_min_insn_size (insn) + sum) >= 48) |
532 | /* Window 1 is full. Go for next window. */ |
533 | return true; |
534 | } |
535 | |
536 | num_restrict = count_num_restricted (insn, window_list); |
537 | |
538 | if (num_restrict > num_allowable_groups[group]) |
539 | return false; |
540 | |
541 | /* See if it fits in the first window. */ |
542 | if (window_list->window_num == 0) |
543 | { |
544 | /* The first widow should have only single and double path |
545 | uops. */ |
546 | if (path == path_double |
547 | && (window_list->num_uops + 2) > MAX_INSN) |
548 | return false; |
549 | else if (path != path_single) |
550 | return false; |
551 | } |
552 | return true; |
553 | } |
554 | |
555 | /* Add an instruction INSN with NUM_UOPS micro-operations to the |
556 | dispatch window WINDOW_LIST. */ |
557 | |
558 | static void |
559 | add_insn_window (rtx_insn *insn, dispatch_windows *window_list, int num_uops) |
560 | { |
561 | int byte_len = ix86_min_insn_size (insn); |
562 | int num_insn = window_list->num_insn; |
563 | int imm_size; |
564 | sched_insn_info *window = window_list->window; |
565 | enum dispatch_group group = get_insn_group (insn); |
566 | enum insn_path path = get_insn_path (insn); |
567 | int num_imm_operand; |
568 | int num_imm32_operand; |
569 | int num_imm64_operand; |
570 | |
571 | if (!window_list->violation && group != disp_cmp |
572 | && !fits_dispatch_window (insn)) |
573 | window_list->violation = true; |
574 | |
575 | imm_size = get_num_immediates (insn, imm: &num_imm_operand, imm32: &num_imm32_operand, |
576 | imm64: &num_imm64_operand); |
577 | |
578 | /* Initialize window with new instruction. */ |
579 | window[num_insn].insn = insn; |
580 | window[num_insn].byte_len = byte_len; |
581 | window[num_insn].group = group; |
582 | window[num_insn].path = path; |
583 | window[num_insn].imm_bytes = imm_size; |
584 | |
585 | window_list->window_size += byte_len; |
586 | window_list->num_insn = num_insn + 1; |
587 | window_list->num_uops = window_list->num_uops + num_uops; |
588 | window_list->imm_size += imm_size; |
589 | window_list->num_imm += num_imm_operand; |
590 | window_list->num_imm_32 += num_imm32_operand; |
591 | window_list->num_imm_64 += num_imm64_operand; |
592 | |
593 | if (group == disp_store) |
594 | window_list->num_stores += 1; |
595 | else if (group == disp_load |
596 | || group == disp_prefetch) |
597 | window_list->num_loads += 1; |
598 | else if (group == disp_load_store) |
599 | { |
600 | window_list->num_stores += 1; |
601 | window_list->num_loads += 1; |
602 | } |
603 | } |
604 | |
605 | /* Adds a scheduled instruction, INSN, to the current dispatch window. |
606 | If the total bytes of instructions or the number of instructions in |
607 | the window exceed allowable, it allocates a new window. */ |
608 | |
609 | static void |
610 | add_to_dispatch_window (rtx_insn *insn) |
611 | { |
612 | int byte_len; |
613 | dispatch_windows *window_list; |
614 | dispatch_windows *next_list; |
615 | dispatch_windows *window0_list; |
616 | enum insn_path path; |
617 | enum dispatch_group insn_group; |
618 | bool insn_fits; |
619 | int num_insn; |
620 | int num_uops; |
621 | int window_num; |
622 | int insn_num_uops; |
623 | int sum; |
624 | |
625 | if (INSN_CODE (insn) < 0) |
626 | return; |
627 | |
628 | byte_len = ix86_min_insn_size (insn); |
629 | window_list = dispatch_window_list; |
630 | next_list = window_list->next; |
631 | path = get_insn_path (insn); |
632 | insn_group = get_insn_group (insn); |
633 | |
634 | /* Get the last dispatch window. */ |
635 | if (next_list) |
636 | window_list = dispatch_window_list->next; |
637 | |
638 | if (path == path_single) |
639 | insn_num_uops = 1; |
640 | else if (path == path_double) |
641 | insn_num_uops = 2; |
642 | else |
643 | insn_num_uops = (int) path; |
644 | |
645 | /* If current window is full, get a new window. |
646 | Window number zero is full, if MAX_INSN uops are scheduled in it. |
647 | Window number one is full, if window zero's bytes plus window |
648 | one's bytes is 32, or if the bytes of the new instruction added |
649 | to the total makes it greater than 48, or it has already MAX_INSN |
650 | instructions in it. */ |
651 | num_insn = window_list->num_insn; |
652 | num_uops = window_list->num_uops; |
653 | window_num = window_list->window_num; |
654 | insn_fits = fits_dispatch_window (insn); |
655 | |
656 | if (num_insn >= MAX_INSN |
657 | || num_uops + insn_num_uops > MAX_INSN |
658 | || !(insn_fits)) |
659 | { |
660 | window_num = ~window_num & 1; |
661 | window_list = allocate_next_window (window_num); |
662 | } |
663 | |
664 | if (window_num == 0) |
665 | { |
666 | add_insn_window (insn, window_list, num_uops: insn_num_uops); |
667 | if (window_list->num_insn >= MAX_INSN |
668 | && insn_group == disp_branch) |
669 | { |
670 | process_end_window (); |
671 | return; |
672 | } |
673 | } |
674 | else if (window_num == 1) |
675 | { |
676 | window0_list = window_list->prev; |
677 | sum = window0_list->window_size + window_list->window_size; |
678 | if (sum == 32 |
679 | || (byte_len + sum) >= 48) |
680 | { |
681 | process_end_window (); |
682 | window_list = dispatch_window_list; |
683 | } |
684 | |
685 | add_insn_window (insn, window_list, num_uops: insn_num_uops); |
686 | } |
687 | else |
688 | gcc_unreachable (); |
689 | |
690 | if (is_end_basic_block (group: insn_group)) |
691 | { |
692 | /* End of basic block is reached do end-basic-block process. */ |
693 | process_end_window (); |
694 | return; |
695 | } |
696 | } |
697 | |
698 | /* Print the dispatch window, WINDOW_NUM, to FILE. */ |
699 | |
700 | DEBUG_FUNCTION static void |
701 | debug_dispatch_window_file (FILE *file, int window_num) |
702 | { |
703 | dispatch_windows *list; |
704 | int i; |
705 | |
706 | if (window_num == 0) |
707 | list = dispatch_window_list; |
708 | else |
709 | list = dispatch_window_list1; |
710 | |
711 | fprintf (stream: file, format: "Window #%d:\n" , list->window_num); |
712 | fprintf (stream: file, format: " num_insn = %d, num_uops = %d, window_size = %d\n" , |
713 | list->num_insn, list->num_uops, list->window_size); |
714 | fprintf (stream: file, format: " num_imm = %d, num_imm_32 = %d, num_imm_64 = %d, imm_size = %d\n" , |
715 | list->num_imm, list->num_imm_32, list->num_imm_64, list->imm_size); |
716 | |
717 | fprintf (stream: file, format: " num_loads = %d, num_stores = %d\n" , list->num_loads, |
718 | list->num_stores); |
719 | fprintf (stream: file, format: " insn info:\n" ); |
720 | |
721 | for (i = 0; i < MAX_INSN; i++) |
722 | { |
723 | if (!list->window[i].insn) |
724 | break; |
725 | fprintf (stream: file, format: " group[%d] = %s, insn[%d] = %p, path[%d] = %d byte_len[%d] = %d, imm_bytes[%d] = %d\n" , |
726 | i, group_name[list->window[i].group], |
727 | i, (void *)list->window[i].insn, |
728 | i, list->window[i].path, |
729 | i, list->window[i].byte_len, |
730 | i, list->window[i].imm_bytes); |
731 | } |
732 | } |
733 | |
734 | /* Print to stdout a dispatch window. */ |
735 | |
736 | DEBUG_FUNCTION void |
737 | debug_dispatch_window (int window_num) |
738 | { |
739 | debug_dispatch_window_file (stdout, window_num); |
740 | } |
741 | |
742 | /* Print INSN dispatch information to FILE. */ |
743 | |
744 | DEBUG_FUNCTION static void |
745 | debug_insn_dispatch_info_file (FILE *file, rtx_insn *insn) |
746 | { |
747 | int byte_len; |
748 | enum insn_path path; |
749 | enum dispatch_group group; |
750 | int imm_size; |
751 | int num_imm_operand; |
752 | int num_imm32_operand; |
753 | int num_imm64_operand; |
754 | |
755 | if (INSN_CODE (insn) < 0) |
756 | return; |
757 | |
758 | byte_len = ix86_min_insn_size (insn); |
759 | path = get_insn_path (insn); |
760 | group = get_insn_group (insn); |
761 | imm_size = get_num_immediates (insn, imm: &num_imm_operand, imm32: &num_imm32_operand, |
762 | imm64: &num_imm64_operand); |
763 | |
764 | fprintf (stream: file, format: " insn info:\n" ); |
765 | fprintf (stream: file, format: " group = %s, path = %d, byte_len = %d\n" , |
766 | group_name[group], path, byte_len); |
767 | fprintf (stream: file, format: " num_imm = %d, num_imm_32 = %d, num_imm_64 = %d, imm_size = %d\n" , |
768 | num_imm_operand, num_imm32_operand, num_imm64_operand, imm_size); |
769 | } |
770 | |
771 | /* Print to STDERR the status of the ready list with respect to |
772 | dispatch windows. */ |
773 | |
774 | DEBUG_FUNCTION void |
775 | debug_ready_dispatch (void) |
776 | { |
777 | int i; |
778 | int no_ready = number_in_ready (); |
779 | |
780 | fprintf (stdout, format: "Number of ready: %d\n" , no_ready); |
781 | |
782 | for (i = 0; i < no_ready; i++) |
783 | debug_insn_dispatch_info_file (stdout, insn: get_ready_element (i)); |
784 | } |
785 | |
786 | /* This routine is the driver of the dispatch scheduler. */ |
787 | |
788 | void |
789 | ix86_bd_do_dispatch (rtx_insn *insn, int mode) |
790 | { |
791 | if (mode == DISPATCH_INIT) |
792 | init_dispatch_sched (); |
793 | else if (mode == ADD_TO_DISPATCH_WINDOW) |
794 | add_to_dispatch_window (insn); |
795 | } |
796 | |
797 | /* Return TRUE if Dispatch Scheduling is supported. */ |
798 | |
799 | bool |
800 | ix86_bd_has_dispatch (rtx_insn *insn, int action) |
801 | { |
802 | /* Current implementation of dispatch scheduler models buldozer only. */ |
803 | if ((TARGET_CPU_P (BDVER1) || TARGET_CPU_P (BDVER2) |
804 | || TARGET_CPU_P (BDVER3) || TARGET_CPU_P (BDVER4)) |
805 | && flag_dispatch_scheduler) |
806 | switch (action) |
807 | { |
808 | default: |
809 | return false; |
810 | |
811 | case IS_DISPATCH_ON: |
812 | return true; |
813 | |
814 | case IS_CMP: |
815 | return is_cmp (insn); |
816 | |
817 | case DISPATCH_VIOLATION: |
818 | return dispatch_violation (); |
819 | |
820 | case FITS_DISPATCH_WINDOW: |
821 | return fits_dispatch_window (insn); |
822 | } |
823 | |
824 | return false; |
825 | } |
826 | |