]>
Commit | Line | Data |
---|---|---|
3edab22e | 1 | /* relax-opt pass of Andes NDS32 cpu for GNU compiler |
fbd26352 | 2 | Copyright (C) 2012-2019 Free Software Foundation, Inc. |
3edab22e | 3 | Contributed by Andes Technology Corporation. |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU General Public License as published | |
9 | by the Free Software Foundation; either version 3, or (at your | |
10 | option) any later version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
15 | License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | /* ------------------------------------------------------------------------ */ | |
22 | ||
23 | #define IN_TARGET_CODE 1 | |
24 | ||
25 | #include "config.h" | |
26 | #include "system.h" | |
27 | #include "coretypes.h" | |
28 | #include "backend.h" | |
29 | #include "target.h" | |
30 | #include "rtl.h" | |
31 | #include "tree.h" | |
32 | #include "stringpool.h" | |
33 | #include "attribs.h" | |
34 | #include "df.h" | |
35 | #include "memmodel.h" | |
36 | #include "tm_p.h" | |
37 | #include "optabs.h" /* For GEN_FCN. */ | |
38 | #include "regs.h" | |
39 | #include "emit-rtl.h" | |
40 | #include "recog.h" | |
41 | #include "diagnostic-core.h" | |
42 | #include "stor-layout.h" | |
43 | #include "varasm.h" | |
44 | #include "calls.h" | |
45 | #include "output.h" | |
46 | #include "explow.h" | |
47 | #include "expr.h" | |
48 | #include "tm-constrs.h" | |
49 | #include "builtins.h" | |
50 | #include "cpplib.h" | |
51 | #include "insn-attr.h" | |
52 | #include "cfgrtl.h" | |
53 | #include "tree-pass.h" | |
54 | ||
9fbdd630 | 55 | using namespace nds32; |
56 | ||
3edab22e | 57 | /* This is used to create unique relax hint id value. |
58 | The initial value is 0. */ | |
59 | static int relax_group_id = 0; | |
60 | ||
61 | /* Group the following pattern as relax candidates: | |
62 | ||
63 | 1. sethi $ra, hi20(sym) | |
64 | ori $ra, $ra, lo12(sym) | |
65 | ==> | |
66 | addi.gp $ra, sym | |
67 | ||
68 | 2. sethi $ra, hi20(sym) | |
69 | lwi $rb, [$ra + lo12(sym)] | |
70 | ==> | |
71 | lwi.gp $rb, [(sym)] | |
72 | ||
73 | 3. sethi $ra, hi20(sym) | |
74 | ori $ra, $ra, lo12(sym) | |
75 | lwi $rb, [$ra] | |
76 | swi $rc, [$ra] | |
77 | ==> | |
78 | lwi37 $rb, [(sym)] | |
79 | swi37 $rc, [(sym)] */ | |
80 | ||
81 | /* Return true if is load/store with REG addressing mode | |
82 | and memory mode is SImode. */ | |
83 | static bool | |
84 | nds32_reg_base_load_store_p (rtx_insn *insn) | |
85 | { | |
86 | rtx mem_src = NULL_RTX; | |
87 | ||
88 | switch (get_attr_type (insn)) | |
89 | { | |
90 | case TYPE_LOAD: | |
91 | mem_src = SET_SRC (PATTERN (insn)); | |
92 | break; | |
93 | case TYPE_STORE: | |
94 | mem_src = SET_DEST (PATTERN (insn)); | |
95 | break; | |
96 | default: | |
97 | break; | |
98 | } | |
99 | ||
100 | /* Find load/store insn with addressing mode is REG. */ | |
101 | if (mem_src != NULL_RTX) | |
102 | { | |
103 | if ((GET_CODE (mem_src) == ZERO_EXTEND) | |
104 | || (GET_CODE (mem_src) == SIGN_EXTEND)) | |
105 | mem_src = XEXP (mem_src, 0); | |
106 | ||
107 | if (GET_CODE (XEXP (mem_src, 0)) == REG) | |
108 | return true; | |
109 | } | |
110 | ||
111 | return false; | |
112 | } | |
113 | ||
114 | /* Return true if insn is a sp/fp base or sp/fp plus load-store instruction. */ | |
115 | ||
116 | static bool | |
117 | nds32_sp_base_or_plus_load_store_p (rtx_insn *insn) | |
118 | { | |
119 | rtx mem_src = NULL_RTX; | |
120 | ||
121 | switch (get_attr_type (insn)) | |
122 | { | |
123 | case TYPE_LOAD: | |
124 | mem_src = SET_SRC (PATTERN (insn)); | |
125 | break; | |
126 | case TYPE_STORE: | |
127 | mem_src = SET_DEST (PATTERN (insn)); | |
128 | break; | |
129 | default: | |
130 | break; | |
131 | } | |
132 | /* Find load/store insn with addressing mode is REG. */ | |
133 | if (mem_src != NULL_RTX) | |
134 | { | |
135 | if ((GET_CODE (mem_src) == ZERO_EXTEND) | |
136 | || (GET_CODE (mem_src) == SIGN_EXTEND)) | |
137 | mem_src = XEXP (mem_src, 0); | |
138 | ||
139 | if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) | |
140 | mem_src = XEXP (mem_src, 0); | |
141 | ||
142 | if (REG_P (XEXP (mem_src, 0)) | |
143 | && ((frame_pointer_needed | |
144 | && REGNO (XEXP (mem_src, 0)) == FP_REGNUM) | |
145 | || REGNO (XEXP (mem_src, 0)) == SP_REGNUM)) | |
146 | return true; | |
147 | } | |
148 | ||
149 | return false; | |
150 | } | |
151 | ||
152 | /* Return true if is load with [REG + REG/CONST_INT] addressing mode. */ | |
153 | static bool | |
154 | nds32_plus_reg_load_store_p (rtx_insn *insn) | |
155 | { | |
156 | rtx mem_src = NULL_RTX; | |
157 | ||
158 | switch (get_attr_type (insn)) | |
159 | { | |
160 | case TYPE_LOAD: | |
161 | mem_src = SET_SRC (PATTERN (insn)); | |
162 | break; | |
163 | case TYPE_STORE: | |
164 | mem_src = SET_DEST (PATTERN (insn)); | |
165 | break; | |
166 | default: | |
167 | break; | |
168 | } | |
169 | ||
170 | /* Find load/store insn with addressing mode is [REG + REG/CONST]. */ | |
171 | if (mem_src != NULL_RTX) | |
172 | { | |
173 | if ((GET_CODE (mem_src) == ZERO_EXTEND) | |
174 | || (GET_CODE (mem_src) == SIGN_EXTEND)) | |
175 | mem_src = XEXP (mem_src, 0); | |
176 | ||
177 | if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) | |
178 | mem_src = XEXP (mem_src, 0); | |
179 | else | |
180 | return false; | |
181 | ||
182 | if (GET_CODE (XEXP (mem_src, 0)) == REG) | |
183 | return true; | |
184 | ||
185 | } | |
186 | ||
187 | return false; | |
188 | } | |
189 | ||
6a7cbd2c | 190 | /* Return true if x is const and the referance is ict symbol. */ |
191 | static bool | |
192 | nds32_ict_const_p (rtx x) | |
193 | { | |
194 | if (GET_CODE (x) == CONST) | |
195 | { | |
196 | x = XEXP (x, 0); | |
197 | return nds32_indirect_call_referenced_p (x); | |
198 | } | |
199 | return FALSE; | |
200 | } | |
9fbdd630 | 201 | |
202 | /* Group the following pattern as relax candidates: | |
203 | ||
204 | GOT: | |
205 | sethi $ra, hi20(sym) | |
206 | ori $ra, $ra, lo12(sym) | |
207 | lw $rb, [$ra + $gp] | |
208 | ||
209 | GOTOFF, TLSLE: | |
210 | sethi $ra, hi20(sym) | |
211 | ori $ra, $ra, lo12(sym) | |
212 | LS $rb, [$ra + $gp] | |
213 | ||
214 | GOTOFF, TLSLE: | |
215 | sethi $ra, hi20(sym) | |
216 | ori $ra, $ra, lo12(sym) | |
217 | add $rb, $ra, $gp($tp) | |
218 | ||
219 | Initial GOT table: | |
220 | sethi $gp,hi20(sym) | |
221 | ori $gp, $gp, lo12(sym) | |
222 | add5.pc $gp */ | |
223 | ||
224 | static auto_vec<rtx_insn *, 32> nds32_group_infos; | |
225 | /* Group the PIC and TLS relax candidate instructions for linker. */ | |
226 | static bool | |
227 | nds32_pic_tls_group (rtx_insn *def_insn, | |
228 | enum nds32_relax_insn_type relax_type, | |
229 | int sym_type) | |
230 | { | |
231 | df_ref def_record; | |
232 | df_link *link; | |
233 | rtx_insn *use_insn = NULL; | |
234 | rtx pat, new_pat; | |
235 | def_record = DF_INSN_DEFS (def_insn); | |
236 | for (link = DF_REF_CHAIN (def_record); link; link = link->next) | |
237 | { | |
238 | if (!DF_REF_INSN_INFO (link->ref)) | |
239 | continue; | |
240 | ||
241 | use_insn = DF_REF_INSN (link->ref); | |
242 | ||
243 | /* Skip if define insn and use insn not in the same basic block. */ | |
244 | if (!dominated_by_p (CDI_DOMINATORS, | |
245 | BLOCK_FOR_INSN (use_insn), | |
246 | BLOCK_FOR_INSN (def_insn))) | |
247 | return FALSE; | |
248 | ||
249 | /* Skip if use_insn not active insn. */ | |
250 | if (!active_insn_p (use_insn)) | |
251 | return FALSE; | |
252 | ||
253 | switch (relax_type) | |
254 | { | |
255 | case RELAX_ORI: | |
256 | ||
257 | /* GOTOFF, TLSLE: | |
258 | sethi $ra, hi20(sym) | |
259 | ori $ra, $ra, lo12(sym) | |
260 | add $rb, $ra, $gp($tp) */ | |
261 | if ((sym_type == UNSPEC_TLSLE | |
262 | || sym_type == UNSPEC_GOTOFF) | |
263 | && (recog_memoized (use_insn) == CODE_FOR_addsi3)) | |
264 | { | |
265 | pat = XEXP (PATTERN (use_insn), 1); | |
266 | new_pat = | |
267 | gen_rtx_UNSPEC (SImode, | |
268 | gen_rtvec (2, XEXP (pat, 0), XEXP (pat, 1)), | |
269 | UNSPEC_ADD32); | |
270 | validate_replace_rtx (pat, new_pat, use_insn); | |
271 | nds32_group_infos.safe_push (use_insn); | |
272 | } | |
273 | else if (nds32_plus_reg_load_store_p (use_insn) | |
274 | && !nds32_sp_base_or_plus_load_store_p (use_insn)) | |
275 | nds32_group_infos.safe_push (use_insn); | |
276 | else | |
277 | return FALSE; | |
278 | break; | |
279 | ||
280 | default: | |
281 | return FALSE; | |
282 | } | |
283 | } | |
284 | return TRUE; | |
285 | } | |
286 | ||
287 | static int | |
288 | nds32_pic_tls_symbol_type (rtx x) | |
289 | { | |
290 | x = XEXP (SET_SRC (PATTERN (x)), 1); | |
291 | ||
292 | if (GET_CODE (x) == CONST) | |
293 | { | |
294 | x = XEXP (x, 0); | |
295 | ||
296 | if (GET_CODE (x) == PLUS) | |
297 | x = XEXP (x, 0); | |
298 | ||
299 | return XINT (x, 1); | |
300 | } | |
301 | ||
302 | return XINT (x, 1); | |
303 | } | |
304 | ||
3edab22e | 305 | /* Group the relax candidates with group id. */ |
306 | static void | |
6b63fbbe | 307 | nds32_group_insns (rtx_insn *sethi) |
3edab22e | 308 | { |
309 | df_ref def_record, use_record; | |
310 | df_link *link; | |
311 | rtx_insn *use_insn = NULL; | |
312 | rtx group_id; | |
9fbdd630 | 313 | bool valid; |
3edab22e | 314 | |
315 | def_record = DF_INSN_DEFS (sethi); | |
316 | ||
317 | for (link = DF_REF_CHAIN (def_record); link; link = link->next) | |
318 | { | |
319 | if (!DF_REF_INSN_INFO (link->ref)) | |
320 | continue; | |
321 | ||
322 | use_insn = DF_REF_INSN (link->ref); | |
323 | ||
324 | /* Skip if define insn and use insn not in the same basic block. */ | |
325 | if (!dominated_by_p (CDI_DOMINATORS, | |
326 | BLOCK_FOR_INSN (use_insn), | |
327 | BLOCK_FOR_INSN (sethi))) | |
328 | return; | |
329 | ||
330 | /* Skip if the low-part used register is from different high-part | |
331 | instructions. */ | |
332 | use_record = DF_INSN_USES (use_insn); | |
333 | if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) | |
334 | return; | |
335 | ||
336 | /* Skip if use_insn not active insn. */ | |
337 | if (!active_insn_p (use_insn)) | |
338 | return; | |
339 | ||
340 | /* Initial use_insn_type. */ | |
341 | if (!(recog_memoized (use_insn) == CODE_FOR_lo_sum | |
342 | || nds32_symbol_load_store_p (use_insn) | |
343 | || (nds32_reg_base_load_store_p (use_insn) | |
344 | &&!nds32_sp_base_or_plus_load_store_p (use_insn)))) | |
345 | return; | |
346 | } | |
347 | ||
348 | group_id = GEN_INT (relax_group_id); | |
349 | /* Insert .relax_* directive for sethi. */ | |
350 | emit_insn_before (gen_relax_group (group_id), sethi); | |
351 | ||
352 | /* Scan the use insns and insert the directive. */ | |
353 | for (link = DF_REF_CHAIN (def_record); link; link = link->next) | |
354 | { | |
355 | if (!DF_REF_INSN_INFO (link->ref)) | |
356 | continue; | |
357 | ||
358 | use_insn = DF_REF_INSN (link->ref); | |
359 | ||
360 | /* Insert .relax_* directive. */ | |
361 | if (active_insn_p (use_insn)) | |
362 | emit_insn_before (gen_relax_group (group_id), use_insn); | |
9fbdd630 | 363 | |
364 | /* Find ori ra, ra, unspec(symbol) instruction. */ | |
365 | if (use_insn != NULL | |
366 | && recog_memoized (use_insn) == CODE_FOR_lo_sum | |
367 | && !nds32_const_unspec_p (XEXP (SET_SRC (PATTERN (use_insn)), 1))) | |
368 | { | |
369 | int sym_type = nds32_pic_tls_symbol_type (use_insn); | |
370 | valid = nds32_pic_tls_group (use_insn, RELAX_ORI, sym_type); | |
371 | ||
372 | /* Insert .relax_* directive. */ | |
373 | while (!nds32_group_infos.is_empty ()) | |
374 | { | |
375 | use_insn = nds32_group_infos.pop (); | |
376 | if (valid) | |
377 | emit_insn_before (gen_relax_group (group_id), use_insn); | |
378 | } | |
379 | } | |
380 | } | |
381 | ||
382 | relax_group_id++; | |
383 | } | |
384 | ||
385 | /* Convert relax group id in rtl. */ | |
386 | ||
387 | static void | |
388 | nds32_group_tls_insn (rtx insn) | |
389 | { | |
390 | rtx pat = PATTERN (insn); | |
391 | rtx unspec_relax_group = XEXP (XVECEXP (pat, 0, 1), 0); | |
392 | ||
393 | while (GET_CODE (pat) != SET && GET_CODE (pat) == PARALLEL) | |
394 | { | |
395 | pat = XVECEXP (pat, 0, 0); | |
396 | } | |
397 | ||
398 | if (GET_CODE (unspec_relax_group) == UNSPEC | |
399 | && XINT (unspec_relax_group, 1) == UNSPEC_VOLATILE_RELAX_GROUP) | |
400 | { | |
401 | XVECEXP (unspec_relax_group, 0, 0) = GEN_INT (relax_group_id); | |
402 | } | |
403 | ||
404 | relax_group_id++; | |
405 | } | |
406 | ||
407 | static bool | |
408 | nds32_float_reg_load_store_p (rtx_insn *insn) | |
409 | { | |
410 | rtx pat = PATTERN (insn); | |
411 | ||
412 | if (get_attr_type (insn) == TYPE_FLOAD | |
413 | && GET_CODE (pat) == SET | |
414 | && (GET_MODE (XEXP (pat, 0)) == SFmode | |
415 | || GET_MODE (XEXP (pat, 0)) == DFmode) | |
416 | && MEM_P (XEXP (pat, 1))) | |
417 | { | |
418 | rtx addr = XEXP (XEXP (pat, 1), 0); | |
419 | ||
420 | /* [$ra] */ | |
421 | if (REG_P (addr)) | |
422 | return true; | |
423 | /* [$ra + offset] */ | |
424 | if (GET_CODE (addr) == PLUS | |
425 | && REG_P (XEXP (addr, 0)) | |
426 | && CONST_INT_P (XEXP (addr, 1))) | |
427 | return true; | |
428 | } | |
429 | return false; | |
430 | } | |
431 | ||
432 | ||
433 | /* Group float load-store instructions: | |
434 | la $ra, symbol | |
435 | flsi $rt, [$ra + offset] */ | |
436 | ||
437 | static void | |
6b63fbbe | 438 | nds32_group_float_insns (rtx_insn *insn) |
9fbdd630 | 439 | { |
440 | df_ref def_record, use_record; | |
441 | df_link *link; | |
442 | rtx_insn *use_insn = NULL; | |
443 | rtx group_id; | |
444 | ||
445 | def_record = DF_INSN_DEFS (insn); | |
446 | ||
447 | for (link = DF_REF_CHAIN (def_record); link; link = link->next) | |
448 | { | |
449 | if (!DF_REF_INSN_INFO (link->ref)) | |
450 | continue; | |
451 | ||
452 | use_insn = DF_REF_INSN (link->ref); | |
453 | ||
454 | /* Skip if define insn and use insn not in the same basic block. */ | |
455 | if (!dominated_by_p (CDI_DOMINATORS, | |
456 | BLOCK_FOR_INSN (use_insn), | |
457 | BLOCK_FOR_INSN (insn))) | |
458 | return; | |
459 | ||
460 | /* Skip if the low-part used register is from different high-part | |
461 | instructions. */ | |
462 | use_record = DF_INSN_USES (use_insn); | |
463 | if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) | |
464 | return; | |
465 | ||
466 | /* Skip if use_insn not active insn. */ | |
467 | if (!active_insn_p (use_insn)) | |
468 | return; | |
469 | ||
470 | if (!nds32_float_reg_load_store_p (use_insn) | |
471 | || find_post_update_rtx (use_insn) != -1) | |
472 | return; | |
473 | } | |
474 | ||
475 | group_id = GEN_INT (relax_group_id); | |
476 | /* Insert .relax_* directive for insn. */ | |
477 | emit_insn_before (gen_relax_group (group_id), insn); | |
478 | ||
479 | /* Scan the use insns and insert the directive. */ | |
480 | for (link = DF_REF_CHAIN (def_record); link; link = link->next) | |
481 | { | |
482 | if (!DF_REF_INSN_INFO (link->ref)) | |
483 | continue; | |
484 | ||
485 | use_insn = DF_REF_INSN (link->ref); | |
486 | ||
487 | /* Insert .relax_* directive. */ | |
488 | emit_insn_before (gen_relax_group (group_id), use_insn); | |
3edab22e | 489 | } |
490 | ||
491 | relax_group_id++; | |
492 | } | |
493 | ||
494 | /* Group the relax candidate instructions for linker. */ | |
495 | static void | |
496 | nds32_relax_group (void) | |
497 | { | |
498 | rtx_insn *insn; | |
499 | ||
500 | compute_bb_for_insn (); | |
501 | ||
502 | df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN); | |
503 | df_insn_rescan_all (); | |
504 | df_analyze (); | |
505 | df_set_flags (DF_DEFER_INSN_RESCAN); | |
506 | calculate_dominance_info (CDI_DOMINATORS); | |
507 | ||
508 | insn = get_insns (); | |
509 | gcc_assert (NOTE_P (insn)); | |
510 | ||
511 | for (insn = next_active_insn (insn); insn; insn = next_active_insn (insn)) | |
512 | { | |
513 | if (NONJUMP_INSN_P (insn)) | |
514 | { | |
515 | /* Find sethi ra, symbol instruction. */ | |
516 | if (recog_memoized (insn) == CODE_FOR_sethi | |
517 | && nds32_symbolic_operand (XEXP (SET_SRC (PATTERN (insn)), 0), | |
6a7cbd2c | 518 | SImode) |
519 | && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) | |
3edab22e | 520 | nds32_group_insns (insn); |
9fbdd630 | 521 | else if (recog_memoized (insn) == CODE_FOR_tls_ie) |
522 | nds32_group_tls_insn (insn); | |
523 | else if (TARGET_FPU_SINGLE | |
524 | && recog_memoized (insn) == CODE_FOR_move_addr | |
525 | && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) | |
526 | { | |
527 | nds32_group_float_insns (insn); | |
528 | } | |
529 | } | |
530 | else if (CALL_P (insn) && recog_memoized (insn) == CODE_FOR_tls_desc) | |
531 | { | |
532 | nds32_group_tls_insn (insn); | |
3edab22e | 533 | } |
534 | } | |
535 | ||
536 | /* We must call df_finish_pass manually because it should be invoked before | |
537 | BB information is destroyed. Hence we cannot set the TODO_df_finish flag | |
538 | to the pass manager. */ | |
539 | df_insn_rescan_all (); | |
540 | df_finish_pass (false); | |
541 | free_dominance_info (CDI_DOMINATORS); | |
542 | } | |
543 | ||
544 | static unsigned int | |
545 | nds32_relax_opt (void) | |
546 | { | |
547 | if (TARGET_RELAX_HINT) | |
548 | nds32_relax_group (); | |
549 | return 1; | |
550 | } | |
551 | ||
552 | const pass_data pass_data_nds32_relax_opt = | |
553 | { | |
554 | RTL_PASS, /* type */ | |
555 | "relax_opt", /* name */ | |
556 | OPTGROUP_NONE, /* optinfo_flags */ | |
557 | TV_MACH_DEP, /* tv_id */ | |
558 | 0, /* properties_required */ | |
559 | 0, /* properties_provided */ | |
560 | 0, /* properties_destroyed */ | |
561 | 0, /* todo_flags_start */ | |
562 | TODO_df_finish, /* todo_flags_finish */ | |
563 | }; | |
564 | ||
565 | class pass_nds32_relax_opt : public rtl_opt_pass | |
566 | { | |
567 | public: | |
568 | pass_nds32_relax_opt (gcc::context *ctxt) | |
569 | : rtl_opt_pass (pass_data_nds32_relax_opt, ctxt) | |
570 | {} | |
571 | ||
572 | /* opt_pass methods: */ | |
573 | bool gate (function *) { return TARGET_RELAX_HINT; } | |
574 | unsigned int execute (function *) { return nds32_relax_opt (); } | |
575 | }; | |
576 | ||
577 | rtl_opt_pass * | |
578 | make_pass_nds32_relax_opt (gcc::context *ctxt) | |
579 | { | |
580 | return new pass_nds32_relax_opt (ctxt); | |
581 | } |