1/* relax-opt pass of Andes NDS32 cpu for GNU compiler
2   Copyright (C) 2012-2022 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
55using namespace nds32;
56
57/* This is used to create unique relax hint id value.
58   The initial value is 0.  */
59static 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
81int
82nds32_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.  */
89static bool
90nds32_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
122static bool
123nds32_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.  */
159static bool
160nds32_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.  */
197static bool
198nds32_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
230static auto_vec<rtx_insn *, 32> nds32_group_infos;
231/* Group the PIC and TLS relax candidate instructions for linker.  */
232static bool
233nds32_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
293static int
294nds32_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.  */
312static void
313nds32_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
391static void
392nds32_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
410static bool
411nds32_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
440static void
441nds32_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.  */
496static void
497nds32_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
545static unsigned int
546nds32_relax_opt (void)
547{
548  if (TARGET_RELAX_HINT)
549    nds32_relax_group ();
550  return 1;
551}
552
553const 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
566class pass_nds32_relax_opt : public rtl_opt_pass
567{
568public:
569  pass_nds32_relax_opt (gcc::context *ctxt)
570    : rtl_opt_pass (pass_data_nds32_relax_opt, ctxt)
571  {}
572
573  /* opt_pass methods: */
574  bool gate (function *) { return TARGET_RELAX_HINT; }
575  unsigned int execute (function *) { return nds32_relax_opt (); }
576};
577
578rtl_opt_pass *
579make_pass_nds32_relax_opt (gcc::context *ctxt)
580{
581  return new pass_nds32_relax_opt (ctxt);
582}
583