1/* The fp-as-gp pass of Andes NDS32 cpu for GNU compiler
2   Copyright (C) 2012-2020 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 "hard-reg-set.h"
30#include "tm_p.h"
31#include "rtl.h"
32#include "memmodel.h"
33#include "emit-rtl.h"
34#include "insn-config.h"
35#include "regs.h"
36#include "hard-reg-set.h"
37#include "ira.h"
38#include "ira-int.h"
39#include "df.h"
40#include "tree-core.h"
41#include "tree-pass.h"
42#include "nds32-protos.h"
43
44/* ------------------------------------------------------------------------ */
45
46/* A helper function to check if this function should contain prologue.  */
47static bool
48nds32_have_prologue_p (void)
49{
50  int i;
51
52  for (i = 0; i < 28; i++)
53    if (NDS32_REQUIRED_CALLEE_SAVED_P (i))
54      return true;
55
56  return (flag_pic
57	  || NDS32_REQUIRED_CALLEE_SAVED_P (FP_REGNUM)
58	  || NDS32_REQUIRED_CALLEE_SAVED_P (LP_REGNUM));
59}
60
61static int
62nds32_get_symbol_count (void)
63{
64  int symbol_count = 0;
65  rtx_insn *insn;
66  basic_block bb;
67
68  FOR_EACH_BB_FN (bb, cfun)
69    {
70      FOR_BB_INSNS (bb, insn)
71	{
72	  /* Counting the insn number which the addressing mode is symbol.  */
73	  if (single_set (insn) && nds32_symbol_load_store_p (insn))
74	    {
75	      rtx pattern = PATTERN (insn);
76	      rtx mem;
77	      gcc_assert (GET_CODE (pattern) == SET);
78	      if (GET_CODE (SET_SRC (pattern)) == REG )
79		mem = SET_DEST (pattern);
80	      else
81		mem = SET_SRC (pattern);
82
83	      /* We have only lwi37 and swi37 for fp-as-gp optimization,
84		 so don't count any other than SImode.
85		 MEM for QImode and HImode will wrap by ZERO_EXTEND
86		 or SIGN_EXTEND */
87	      if (GET_CODE (mem) == MEM)
88		symbol_count++;
89	    }
90	}
91    }
92
93  return symbol_count;
94}
95
96/* Function to determine whether it is worth to do fp_as_gp optimization.
97   Return false: It is NOT worth to do fp_as_gp optimization.
98   Return true: It is APPROXIMATELY worth to do fp_as_gp optimization.
99   Note that if it is worth to do fp_as_gp optimization,
100   we MUST set FP_REGNUM ever live in this function.  */
101static bool
102nds32_fp_as_gp_check_available (void)
103{
104  basic_block bb;
105  basic_block exit_bb;
106  edge_iterator ei;
107  edge e;
108  bool first_exit_blocks_p;
109
110  /* If there exists ANY of following conditions,
111     we DO NOT perform fp_as_gp optimization:
112       1. TARGET_FORBID_FP_AS_GP is set
113	  regardless of the TARGET_FORCE_FP_AS_GP.
114       2. User explicitly uses 'naked'/'no_prologue' attribute.
115	  We use nds32_naked_function_p() to help such checking.
116       3. Not optimize for size.
117       4. Need frame pointer.
118       5. If $fp is already required to be saved,
119	  it means $fp is already choosen by register allocator.
120	  Thus we better not to use it for fp_as_gp optimization.
121       6. This function is a vararg function.
122	  DO NOT apply fp_as_gp optimization on this function
123	  because it may change and break stack frame.
124       7. The epilogue is empty.
125	  This happens when the function uses exit()
126	  or its attribute is no_return.
127	  In that case, compiler will not expand epilogue
128	  so that we have no chance to output .omit_fp_end directive.  */
129  if (TARGET_FORBID_FP_AS_GP
130      || nds32_naked_function_p (current_function_decl)
131      || !optimize_size
132      || frame_pointer_needed
133      || NDS32_REQUIRED_CALLEE_SAVED_P (FP_REGNUM)
134      || (cfun->stdarg == 1)
135      || (find_fallthru_edge (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == NULL))
136    return false;
137
138  /* Disable fp_as_gp if there is any infinite loop since the fp may
139     reuse in infinite loops by register rename.
140     For check infinite loops we should make sure exit_bb is post dominate
141     all other basic blocks if there is no infinite loops.  */
142  first_exit_blocks_p = true;
143  exit_bb = NULL;
144
145  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
146    {
147      /* More than one exit block also do not perform fp_as_gp optimization.  */
148      if (!first_exit_blocks_p)
149	return false;
150
151      exit_bb = e->src;
152      first_exit_blocks_p = false;
153    }
154
155  /* Not found exit_bb? just abort fp_as_gp!  */
156  if (!exit_bb)
157    return false;
158
159  /* Each bb should post dominate by exit_bb if there is no infinite loop! */
160  FOR_EACH_BB_FN (bb, cfun)
161    {
162      if (!dominated_by_p (CDI_POST_DOMINATORS,
163			   bb,
164			   exit_bb))
165	return false;
166    }
167
168  /* Now we can check the possibility of using fp_as_gp optimization.  */
169  if (TARGET_FORCE_FP_AS_GP)
170    {
171      /* User explicitly issues -mforce-fp-as-gp option.  */
172      return true;
173    }
174  else
175    {
176      /* In the following we are going to evaluate whether
177	 it is worth to do fp_as_gp optimization.  */
178      bool good_gain = false;
179      int symbol_count;
180
181      int threshold;
182
183      /* We check if there already requires prologue.
184	 Note that $gp will be saved in prologue for PIC code generation.
185	 After that, we can set threshold by the existence of prologue.
186	 Each fp-implied instruction will gain 2-byte code size
187	 from gp-aware instruction, so we have following heuristics.  */
188      if (flag_pic
189	  || nds32_have_prologue_p ())
190	{
191	  /* Have-prologue:
192	       Compiler already intends to generate prologue content,
193	       so the fp_as_gp optimization will only insert
194	       'la $fp,_FP_BASE_' instruction, which will be
195	       converted into 4-byte instruction at link time.
196	       The threshold is "3" symbol accesses, 2 + 2 + 2 > 4.  */
197	  threshold = 3;
198	}
199      else
200	{
201	  /* None-prologue:
202	       Compiler originally does not generate prologue content,
203	       so the fp_as_gp optimization will NOT ONLY insert
204	       'la $fp,_FP_BASE' instruction, but also causes
205	       push/pop instructions.
206	       If we are using v3push (push25/pop25),
207	       the threshold is "5" symbol accesses, 5*2 > 4 + 2 + 2;
208	       If we are using normal push (smw/lmw),
209	       the threshold is "5+2" symbol accesses 7*2 > 4 + 4 + 4.  */
210	  threshold = 5 + (TARGET_V3PUSH ? 0 : 2);
211	}
212
213      symbol_count = nds32_get_symbol_count ();
214
215      if (symbol_count >= threshold)
216	good_gain = true;
217
218      /* Enable fp_as_gp optimization when potential gain is good enough.  */
219      return good_gain;
220    }
221}
222
223static unsigned int
224nds32_fp_as_gp (void)
225{
226  bool fp_as_gp_p;
227  calculate_dominance_info (CDI_POST_DOMINATORS);
228  fp_as_gp_p = nds32_fp_as_gp_check_available ();
229
230  /* Here is a hack to IRA for enable/disable a hard register per function.
231     We *MUST* review this way after migrate gcc 4.9! */
232  if (fp_as_gp_p) {
233    SET_HARD_REG_BIT(this_target_ira_int->x_no_unit_alloc_regs, FP_REGNUM);
234    df_set_regs_ever_live (FP_REGNUM, 1);
235  } else {
236    CLEAR_HARD_REG_BIT(this_target_ira_int->x_no_unit_alloc_regs, FP_REGNUM);
237  }
238
239  cfun->machine->fp_as_gp_p = fp_as_gp_p;
240
241  free_dominance_info (CDI_POST_DOMINATORS);
242  return 1;
243}
244
245const pass_data pass_data_nds32_fp_as_gp =
246{
247  RTL_PASS,				/* type */
248  "fp_as_gp",				/* name */
249  OPTGROUP_NONE,			/* optinfo_flags */
250  TV_MACH_DEP,				/* tv_id */
251  0,					/* properties_required */
252  0,					/* properties_provided */
253  0,					/* properties_destroyed */
254  0,					/* todo_flags_start */
255  0					/* todo_flags_finish */
256};
257
258class pass_nds32_fp_as_gp : public rtl_opt_pass
259{
260public:
261  pass_nds32_fp_as_gp (gcc::context *ctxt)
262    : rtl_opt_pass (pass_data_nds32_fp_as_gp, ctxt)
263  {}
264
265  /* opt_pass methods: */
266  bool gate (function *)
267  {
268    return !TARGET_LINUX_ABI
269	   && TARGET_16_BIT
270	   && optimize_size;
271  }
272  unsigned int execute (function *) { return nds32_fp_as_gp (); }
273};
274
275rtl_opt_pass *
276make_pass_nds32_fp_as_gp (gcc::context *ctxt)
277{
278  return new pass_nds32_fp_as_gp (ctxt);
279}
280
281/* ------------------------------------------------------------------------ */
282