xref: /netbsd-src/external/gpl3/gcc/dist/gcc/config/riscv/riscv-shorten-memrefs.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Shorten memrefs pass for RISC-V.
2    Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10 
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19 
20 #define IN_TARGET_CODE 1
21 
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "tm.h"
26 #include "rtl.h"
27 #include "backend.h"
28 #include "regs.h"
29 #include "target.h"
30 #include "memmodel.h"
31 #include "emit-rtl.h"
32 #include "df.h"
33 #include "predict.h"
34 #include "tree-pass.h"
35 
36 /* Try to make more use of compressed load and store instructions by replacing
37    a load/store at address BASE + LARGE_OFFSET with a new load/store at address
38    NEW BASE + SMALL OFFSET.  If NEW BASE is stored in a compressed register, the
39    load/store can be compressed.  Since creating NEW BASE incurs an overhead,
40    the change is only attempted when BASE is referenced by at least four
41    load/stores in the same basic block.  */
42 
43 namespace {
44 
45 const pass_data pass_data_shorten_memrefs =
46 {
47   RTL_PASS, /* type */
48   "shorten_memrefs", /* name */
49   OPTGROUP_NONE, /* optinfo_flags */
50   TV_NONE, /* tv_id */
51   0, /* properties_required */
52   0, /* properties_provided */
53   0, /* properties_destroyed */
54   0, /* todo_flags_start */
55   0, /* todo_flags_finish */
56 };
57 
58 class pass_shorten_memrefs : public rtl_opt_pass
59 {
60 public:
pass_shorten_memrefs(gcc::context * ctxt)61   pass_shorten_memrefs (gcc::context *ctxt)
62     : rtl_opt_pass (pass_data_shorten_memrefs, ctxt)
63   {}
64 
65   /* opt_pass methods: */
gate(function *)66   virtual bool gate (function *)
67     {
68       return TARGET_RVC && riscv_mshorten_memrefs && optimize > 0;
69     }
70   virtual unsigned int execute (function *);
71 
72 private:
73   typedef int_hash <HOST_WIDE_INT, 0> regno_hash;
74   typedef hash_map <regno_hash, int> regno_map;
75 
76   regno_map * analyze (basic_block bb);
77   void transform (regno_map *m, basic_block bb);
78   bool get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend);
79 }; // class pass_shorten_memrefs
80 
81 bool
get_si_mem_base_reg(rtx mem,rtx * addr,bool * extend)82 pass_shorten_memrefs::get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend)
83 {
84   /* Whether it's sign/zero extended.  */
85   if (GET_CODE (mem) == ZERO_EXTEND || GET_CODE (mem) == SIGN_EXTEND)
86     {
87       *extend = true;
88       mem = XEXP (mem, 0);
89     }
90 
91   if (!MEM_P (mem) || GET_MODE (mem) != SImode)
92     return false;
93   *addr = XEXP (mem, 0);
94   return GET_CODE (*addr) == PLUS && REG_P (XEXP (*addr, 0));
95 }
96 
97 /* Count how many times each regno is referenced as base address for a memory
98    access.  */
99 
100 pass_shorten_memrefs::regno_map *
analyze(basic_block bb)101 pass_shorten_memrefs::analyze (basic_block bb)
102 {
103   regno_map *m = hash_map<regno_hash, int>::create_ggc (10);
104   rtx_insn *insn;
105 
106   regstat_init_n_sets_and_refs ();
107 
108   FOR_BB_INSNS (bb, insn)
109     {
110       if (!NONJUMP_INSN_P (insn))
111 	continue;
112       rtx pat = PATTERN (insn);
113       if (GET_CODE (pat) != SET)
114 	continue;
115       /* Analyze stores first then loads.  */
116       for (int i = 0; i < 2; i++)
117 	{
118 	  rtx mem = XEXP (pat, i);
119 	  rtx addr;
120 	  bool extend = false;
121 	  if (get_si_mem_base_reg (mem, &addr, &extend))
122 	    {
123 	      HOST_WIDE_INT regno = REGNO (XEXP (addr, 0));
124 	      /* Do not count store zero as these cannot be compressed.  */
125 	      if (i == 0)
126 		{
127 		  if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1))))
128 		    continue;
129 		}
130 	      if (REG_N_REFS (regno) < 4)
131 		continue;
132 	      m->get_or_insert (regno)++;
133 	    }
134 	  }
135     }
136   regstat_free_n_sets_and_refs ();
137 
138   return m;
139 }
140 
141 /* Convert BASE + LARGE_OFFSET to NEW_BASE + SMALL_OFFSET for each load/store
142    with a base reg referenced at least 4 times.  */
143 
144 void
transform(regno_map * m,basic_block bb)145 pass_shorten_memrefs::transform (regno_map *m, basic_block bb)
146 {
147   rtx_insn *insn;
148   FOR_BB_INSNS (bb, insn)
149     {
150       if (!NONJUMP_INSN_P (insn))
151 	continue;
152       rtx pat = PATTERN (insn);
153       if (GET_CODE (pat) != SET)
154 	continue;
155       start_sequence ();
156       /* Transform stores first then loads.  */
157       for (int i = 0; i < 2; i++)
158 	{
159 	  rtx mem = XEXP (pat, i);
160 	  rtx addr;
161 	  bool extend = false;
162 	  if (get_si_mem_base_reg (mem, &addr, &extend))
163 	    {
164 	      HOST_WIDE_INT regno = REGNO (XEXP (addr, 0));
165 	      /* Do not transform store zero as these cannot be compressed.  */
166 	      if (i == 0)
167 		{
168 		  if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1))))
169 		    continue;
170 		}
171 	      if (m->get_or_insert (regno) > 3)
172 		{
173 		  if (extend)
174 		    {
175 		      addr
176 			= targetm.legitimize_address (addr, addr,
177 						      GET_MODE (XEXP (mem, 0)));
178 		      XEXP (XEXP (pat, i), 0)
179 			= replace_equiv_address (XEXP (mem, 0), addr);
180 		    }
181 		  else
182 		    {
183 		      addr = targetm.legitimize_address (addr, addr,
184 							 GET_MODE (mem));
185 		      XEXP (pat, i) = replace_equiv_address (mem, addr);
186 		    }
187 		  df_insn_rescan (insn);
188 		}
189 	    }
190 	}
191       rtx_insn *seq = get_insns ();
192       end_sequence ();
193       emit_insn_before (seq, insn);
194     }
195 }
196 
197 unsigned int
execute(function * fn)198 pass_shorten_memrefs::execute (function *fn)
199 {
200   basic_block bb;
201 
202   FOR_ALL_BB_FN (bb, fn)
203   {
204     regno_map *m;
205     if (optimize_bb_for_speed_p (bb))
206       continue;
207     m = analyze (bb);
208     transform (m, bb);
209   }
210 
211   return 0;
212 }
213 
214 } // anon namespace
215 
216 rtl_opt_pass *
make_pass_shorten_memrefs(gcc::context * ctxt)217 make_pass_shorten_memrefs (gcc::context *ctxt)
218 {
219   return new pass_shorten_memrefs (ctxt);
220 }
221