Mercurial > hg > CbC > CbC_gcc
comparison gcc/config/nds32/nds32-relax-opt.c @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | |
children | 1830386684a0 |
comparison
equal
deleted
inserted
replaced
111:04ced10e8804 | 131:84e7813d76e9 |
---|---|
1 /* relax-opt pass of Andes NDS32 cpu for GNU compiler | |
2 Copyright (C) 2012-2018 Free Software Foundation, Inc. | |
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 | |
55 using namespace nds32; | |
56 | |
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 | |
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 } | |
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 | |
305 /* Group the relax candidates with group id. */ | |
306 static void | |
307 nds32_group_insns (rtx_insn *sethi) | |
308 { | |
309 df_ref def_record, use_record; | |
310 df_link *link; | |
311 rtx_insn *use_insn = NULL; | |
312 rtx group_id; | |
313 bool valid; | |
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); | |
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 | |
438 nds32_group_float_insns (rtx_insn *insn) | |
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); | |
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), | |
518 SImode) | |
519 && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) | |
520 nds32_group_insns (insn); | |
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); | |
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 } |