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