1 /* relax-opt pass of Andes NDS32 cpu for GNU compiler
2 Copyright (C) 2012-2021 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 int
nds32_alloc_relax_group_id()82 nds32_alloc_relax_group_id ()
83 {
84 return relax_group_id++;
85 }
86
87 /* Return true if is load/store with REG addressing mode
88 and memory mode is SImode. */
89 static bool
nds32_reg_base_load_store_p(rtx_insn * insn)90 nds32_reg_base_load_store_p (rtx_insn *insn)
91 {
92 rtx mem_src = NULL_RTX;
93
94 switch (get_attr_type (insn))
95 {
96 case TYPE_LOAD:
97 mem_src = SET_SRC (PATTERN (insn));
98 break;
99 case TYPE_STORE:
100 mem_src = SET_DEST (PATTERN (insn));
101 break;
102 default:
103 break;
104 }
105
106 /* Find load/store insn with addressing mode is REG. */
107 if (mem_src != NULL_RTX)
108 {
109 if ((GET_CODE (mem_src) == ZERO_EXTEND)
110 || (GET_CODE (mem_src) == SIGN_EXTEND))
111 mem_src = XEXP (mem_src, 0);
112
113 if (GET_CODE (XEXP (mem_src, 0)) == REG)
114 return true;
115 }
116
117 return false;
118 }
119
120 /* Return true if insn is a sp/fp base or sp/fp plus load-store instruction. */
121
122 static bool
nds32_sp_base_or_plus_load_store_p(rtx_insn * insn)123 nds32_sp_base_or_plus_load_store_p (rtx_insn *insn)
124 {
125 rtx mem_src = NULL_RTX;
126
127 switch (get_attr_type (insn))
128 {
129 case TYPE_LOAD:
130 mem_src = SET_SRC (PATTERN (insn));
131 break;
132 case TYPE_STORE:
133 mem_src = SET_DEST (PATTERN (insn));
134 break;
135 default:
136 break;
137 }
138 /* Find load/store insn with addressing mode is REG. */
139 if (mem_src != NULL_RTX)
140 {
141 if ((GET_CODE (mem_src) == ZERO_EXTEND)
142 || (GET_CODE (mem_src) == SIGN_EXTEND))
143 mem_src = XEXP (mem_src, 0);
144
145 if ((GET_CODE (XEXP (mem_src, 0)) == PLUS))
146 mem_src = XEXP (mem_src, 0);
147
148 if (REG_P (XEXP (mem_src, 0))
149 && ((frame_pointer_needed
150 && REGNO (XEXP (mem_src, 0)) == FP_REGNUM)
151 || REGNO (XEXP (mem_src, 0)) == SP_REGNUM))
152 return true;
153 }
154
155 return false;
156 }
157
158 /* Return true if is load with [REG + REG/CONST_INT] addressing mode. */
159 static bool
nds32_plus_reg_load_store_p(rtx_insn * insn)160 nds32_plus_reg_load_store_p (rtx_insn *insn)
161 {
162 rtx mem_src = NULL_RTX;
163
164 switch (get_attr_type (insn))
165 {
166 case TYPE_LOAD:
167 mem_src = SET_SRC (PATTERN (insn));
168 break;
169 case TYPE_STORE:
170 mem_src = SET_DEST (PATTERN (insn));
171 break;
172 default:
173 break;
174 }
175
176 /* Find load/store insn with addressing mode is [REG + REG/CONST]. */
177 if (mem_src != NULL_RTX)
178 {
179 if ((GET_CODE (mem_src) == ZERO_EXTEND)
180 || (GET_CODE (mem_src) == SIGN_EXTEND))
181 mem_src = XEXP (mem_src, 0);
182
183 if ((GET_CODE (XEXP (mem_src, 0)) == PLUS))
184 mem_src = XEXP (mem_src, 0);
185 else
186 return false;
187
188 if (GET_CODE (XEXP (mem_src, 0)) == REG)
189 return true;
190
191 }
192
193 return false;
194 }
195
196 /* Return true if x is const and the referance is ict symbol. */
197 static bool
nds32_ict_const_p(rtx x)198 nds32_ict_const_p (rtx x)
199 {
200 if (GET_CODE (x) == CONST)
201 {
202 x = XEXP (x, 0);
203 return nds32_indirect_call_referenced_p (x);
204 }
205 return FALSE;
206 }
207
208 /* Group the following pattern as relax candidates:
209
210 GOT:
211 sethi $ra, hi20(sym)
212 ori $ra, $ra, lo12(sym)
213 lw $rb, [$ra + $gp]
214
215 GOTOFF, TLSLE:
216 sethi $ra, hi20(sym)
217 ori $ra, $ra, lo12(sym)
218 LS $rb, [$ra + $gp]
219
220 GOTOFF, TLSLE:
221 sethi $ra, hi20(sym)
222 ori $ra, $ra, lo12(sym)
223 add $rb, $ra, $gp($tp)
224
225 Initial GOT table:
226 sethi $gp,hi20(sym)
227 ori $gp, $gp, lo12(sym)
228 add5.pc $gp */
229
230 static auto_vec<rtx_insn *, 32> nds32_group_infos;
231 /* Group the PIC and TLS relax candidate instructions for linker. */
232 static bool
nds32_pic_tls_group(rtx_insn * def_insn,enum nds32_relax_insn_type relax_type,int sym_type)233 nds32_pic_tls_group (rtx_insn *def_insn,
234 enum nds32_relax_insn_type relax_type,
235 int sym_type)
236 {
237 df_ref def_record;
238 df_link *link;
239 rtx_insn *use_insn = NULL;
240 rtx pat, new_pat;
241 def_record = DF_INSN_DEFS (def_insn);
242 for (link = DF_REF_CHAIN (def_record); link; link = link->next)
243 {
244 if (!DF_REF_INSN_INFO (link->ref))
245 continue;
246
247 use_insn = DF_REF_INSN (link->ref);
248
249 /* Skip if define insn and use insn not in the same basic block. */
250 if (!dominated_by_p (CDI_DOMINATORS,
251 BLOCK_FOR_INSN (use_insn),
252 BLOCK_FOR_INSN (def_insn)))
253 return FALSE;
254
255 /* Skip if use_insn not active insn. */
256 if (!active_insn_p (use_insn))
257 return FALSE;
258
259 switch (relax_type)
260 {
261 case RELAX_ORI:
262
263 /* GOTOFF, TLSLE:
264 sethi $ra, hi20(sym)
265 ori $ra, $ra, lo12(sym)
266 add $rb, $ra, $gp($tp) */
267 if ((sym_type == UNSPEC_TLSLE
268 || sym_type == UNSPEC_GOTOFF)
269 && (recog_memoized (use_insn) == CODE_FOR_addsi3))
270 {
271 pat = XEXP (PATTERN (use_insn), 1);
272 new_pat =
273 gen_rtx_UNSPEC (SImode,
274 gen_rtvec (2, XEXP (pat, 0), XEXP (pat, 1)),
275 UNSPEC_ADD32);
276 validate_replace_rtx (pat, new_pat, use_insn);
277 nds32_group_infos.safe_push (use_insn);
278 }
279 else if (nds32_plus_reg_load_store_p (use_insn)
280 && !nds32_sp_base_or_plus_load_store_p (use_insn))
281 nds32_group_infos.safe_push (use_insn);
282 else
283 return FALSE;
284 break;
285
286 default:
287 return FALSE;
288 }
289 }
290 return TRUE;
291 }
292
293 static int
nds32_pic_tls_symbol_type(rtx x)294 nds32_pic_tls_symbol_type (rtx x)
295 {
296 x = XEXP (SET_SRC (PATTERN (x)), 1);
297
298 if (GET_CODE (x) == CONST)
299 {
300 x = XEXP (x, 0);
301
302 if (GET_CODE (x) == PLUS)
303 x = XEXP (x, 0);
304
305 return XINT (x, 1);
306 }
307
308 return XINT (x, 1);
309 }
310
311 /* Group the relax candidates with group id. */
312 static void
nds32_group_insns(rtx_insn * sethi)313 nds32_group_insns (rtx_insn *sethi)
314 {
315 df_ref def_record, use_record;
316 df_link *link;
317 rtx_insn *use_insn = NULL;
318 rtx group_id;
319 bool valid;
320
321 def_record = DF_INSN_DEFS (sethi);
322
323 for (link = DF_REF_CHAIN (def_record); link; link = link->next)
324 {
325 if (!DF_REF_INSN_INFO (link->ref))
326 continue;
327
328 use_insn = DF_REF_INSN (link->ref);
329
330 /* Skip if define insn and use insn not in the same basic block. */
331 if (!dominated_by_p (CDI_DOMINATORS,
332 BLOCK_FOR_INSN (use_insn),
333 BLOCK_FOR_INSN (sethi)))
334 return;
335
336 /* Skip if the low-part used register is from different high-part
337 instructions. */
338 use_record = DF_INSN_USES (use_insn);
339 if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next)
340 return;
341
342 /* Skip if use_insn not active insn. */
343 if (!active_insn_p (use_insn))
344 return;
345
346 /* Initial use_insn_type. */
347 if (!(recog_memoized (use_insn) == CODE_FOR_lo_sum
348 || nds32_symbol_load_store_p (use_insn)
349 || (nds32_reg_base_load_store_p (use_insn)
350 &&!nds32_sp_base_or_plus_load_store_p (use_insn))))
351 return;
352 }
353
354 group_id = GEN_INT (nds32_alloc_relax_group_id ());
355 /* Insert .relax_* directive for sethi. */
356 emit_insn_before (gen_relax_group (group_id), sethi);
357
358 /* Scan the use insns and insert the directive. */
359 for (link = DF_REF_CHAIN (def_record); link; link = link->next)
360 {
361 if (!DF_REF_INSN_INFO (link->ref))
362 continue;
363
364 use_insn = DF_REF_INSN (link->ref);
365
366 /* Insert .relax_* directive. */
367 if (active_insn_p (use_insn))
368 emit_insn_before (gen_relax_group (group_id), use_insn);
369
370 /* Find ori ra, ra, unspec(symbol) instruction. */
371 if (use_insn != NULL
372 && recog_memoized (use_insn) == CODE_FOR_lo_sum
373 && !nds32_const_unspec_p (XEXP (SET_SRC (PATTERN (use_insn)), 1)))
374 {
375 int sym_type = nds32_pic_tls_symbol_type (use_insn);
376 valid = nds32_pic_tls_group (use_insn, RELAX_ORI, sym_type);
377
378 /* Insert .relax_* directive. */
379 while (!nds32_group_infos.is_empty ())
380 {
381 use_insn = nds32_group_infos.pop ();
382 if (valid)
383 emit_insn_before (gen_relax_group (group_id), use_insn);
384 }
385 }
386 }
387 }
388
389 /* Convert relax group id in rtl. */
390
391 static void
nds32_group_tls_insn(rtx insn)392 nds32_group_tls_insn (rtx insn)
393 {
394 rtx pat = PATTERN (insn);
395 rtx unspec_relax_group = XEXP (XVECEXP (pat, 0, 1), 0);
396 int group_id = nds32_alloc_relax_group_id ();
397
398 while (GET_CODE (pat) != SET && GET_CODE (pat) == PARALLEL)
399 {
400 pat = XVECEXP (pat, 0, 0);
401 }
402
403 if (GET_CODE (unspec_relax_group) == UNSPEC
404 && XINT (unspec_relax_group, 1) == UNSPEC_VOLATILE_RELAX_GROUP)
405 {
406 XVECEXP (unspec_relax_group, 0, 0) = GEN_INT (group_id);
407 }
408 }
409
410 static bool
nds32_float_reg_load_store_p(rtx_insn * insn)411 nds32_float_reg_load_store_p (rtx_insn *insn)
412 {
413 rtx pat = PATTERN (insn);
414
415 if (get_attr_type (insn) == TYPE_FLOAD
416 && GET_CODE (pat) == SET
417 && (GET_MODE (XEXP (pat, 0)) == SFmode
418 || GET_MODE (XEXP (pat, 0)) == DFmode)
419 && MEM_P (XEXP (pat, 1)))
420 {
421 rtx addr = XEXP (XEXP (pat, 1), 0);
422
423 /* [$ra] */
424 if (REG_P (addr))
425 return true;
426 /* [$ra + offset] */
427 if (GET_CODE (addr) == PLUS
428 && REG_P (XEXP (addr, 0))
429 && CONST_INT_P (XEXP (addr, 1)))
430 return true;
431 }
432 return false;
433 }
434
435
436 /* Group float load-store instructions:
437 la $ra, symbol
438 flsi $rt, [$ra + offset] */
439
440 static void
nds32_group_float_insns(rtx_insn * insn)441 nds32_group_float_insns (rtx_insn *insn)
442 {
443 df_ref def_record, use_record;
444 df_link *link;
445 rtx_insn *use_insn = NULL;
446 rtx group_id;
447
448 def_record = DF_INSN_DEFS (insn);
449
450 for (link = DF_REF_CHAIN (def_record); link; link = link->next)
451 {
452 if (!DF_REF_INSN_INFO (link->ref))
453 continue;
454
455 use_insn = DF_REF_INSN (link->ref);
456
457 /* Skip if define insn and use insn not in the same basic block. */
458 if (!dominated_by_p (CDI_DOMINATORS,
459 BLOCK_FOR_INSN (use_insn),
460 BLOCK_FOR_INSN (insn)))
461 return;
462
463 /* Skip if the low-part used register is from different high-part
464 instructions. */
465 use_record = DF_INSN_USES (use_insn);
466 if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next)
467 return;
468
469 /* Skip if use_insn not active insn. */
470 if (!active_insn_p (use_insn))
471 return;
472
473 if (!nds32_float_reg_load_store_p (use_insn)
474 || find_post_update_rtx (use_insn) != -1)
475 return;
476 }
477
478 group_id = GEN_INT (nds32_alloc_relax_group_id ());
479 /* Insert .relax_* directive for insn. */
480 emit_insn_before (gen_relax_group (group_id), insn);
481
482 /* Scan the use insns and insert the directive. */
483 for (link = DF_REF_CHAIN (def_record); link; link = link->next)
484 {
485 if (!DF_REF_INSN_INFO (link->ref))
486 continue;
487
488 use_insn = DF_REF_INSN (link->ref);
489
490 /* Insert .relax_* directive. */
491 emit_insn_before (gen_relax_group (group_id), use_insn);
492 }
493 }
494
495 /* Group the relax candidate instructions for linker. */
496 static void
nds32_relax_group(void)497 nds32_relax_group (void)
498 {
499 rtx_insn *insn;
500
501 compute_bb_for_insn ();
502
503 df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN);
504 df_insn_rescan_all ();
505 df_analyze ();
506 df_set_flags (DF_DEFER_INSN_RESCAN);
507 calculate_dominance_info (CDI_DOMINATORS);
508
509 insn = get_insns ();
510 gcc_assert (NOTE_P (insn));
511
512 for (insn = next_active_insn (insn); insn; insn = next_active_insn (insn))
513 {
514 if (NONJUMP_INSN_P (insn))
515 {
516 /* Find sethi ra, symbol instruction. */
517 if (recog_memoized (insn) == CODE_FOR_sethi
518 && nds32_symbolic_operand (XEXP (SET_SRC (PATTERN (insn)), 0),
519 SImode)
520 && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0)))
521 nds32_group_insns (insn);
522 else if (recog_memoized (insn) == CODE_FOR_tls_ie)
523 nds32_group_tls_insn (insn);
524 else if (TARGET_FPU_SINGLE
525 && recog_memoized (insn) == CODE_FOR_move_addr
526 && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0)))
527 {
528 nds32_group_float_insns (insn);
529 }
530 }
531 else if (CALL_P (insn) && recog_memoized (insn) == CODE_FOR_tls_desc)
532 {
533 nds32_group_tls_insn (insn);
534 }
535 }
536
537 /* We must call df_finish_pass manually because it should be invoked before
538 BB information is destroyed. Hence we cannot set the TODO_df_finish flag
539 to the pass manager. */
540 df_insn_rescan_all ();
541 df_finish_pass (false);
542 free_dominance_info (CDI_DOMINATORS);
543 }
544
545 static unsigned int
nds32_relax_opt(void)546 nds32_relax_opt (void)
547 {
548 if (TARGET_RELAX_HINT)
549 nds32_relax_group ();
550 return 1;
551 }
552
553 const pass_data pass_data_nds32_relax_opt =
554 {
555 RTL_PASS, /* type */
556 "relax_opt", /* name */
557 OPTGROUP_NONE, /* optinfo_flags */
558 TV_MACH_DEP, /* tv_id */
559 0, /* properties_required */
560 0, /* properties_provided */
561 0, /* properties_destroyed */
562 0, /* todo_flags_start */
563 TODO_df_finish, /* todo_flags_finish */
564 };
565
566 class pass_nds32_relax_opt : public rtl_opt_pass
567 {
568 public:
pass_nds32_relax_opt(gcc::context * ctxt)569 pass_nds32_relax_opt (gcc::context *ctxt)
570 : rtl_opt_pass (pass_data_nds32_relax_opt, ctxt)
571 {}
572
573 /* opt_pass methods: */
gate(function *)574 bool gate (function *) { return TARGET_RELAX_HINT; }
execute(function *)575 unsigned int execute (function *) { return nds32_relax_opt (); }
576 };
577
578 rtl_opt_pass *
make_pass_nds32_relax_opt(gcc::context * ctxt)579 make_pass_nds32_relax_opt (gcc::context *ctxt)
580 {
581 return new pass_nds32_relax_opt (ctxt);
582 }
583