]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/riscv/riscv-shorten-memrefs.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / config / riscv / riscv-shorten-memrefs.cc
1 /* Shorten memrefs pass for RISC-V.
2 Copyright (C) 2018-2024 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:
61 pass_shorten_memrefs (gcc::context *ctxt)
62 : rtl_opt_pass (pass_data_shorten_memrefs, ctxt)
63 {}
64
65 /* opt_pass methods: */
66 virtual bool gate (function *)
67 {
68 return (TARGET_RVC || TARGET_ZCA)
69 && riscv_mshorten_memrefs && optimize > 0;
70 }
71 virtual unsigned int execute (function *);
72
73 private:
74 typedef int_hash <HOST_WIDE_INT, 0> regno_hash;
75 typedef hash_map <regno_hash, int> regno_map;
76
77 regno_map * analyze (basic_block bb);
78 void transform (regno_map *m, basic_block bb);
79 bool get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend);
80 }; // class pass_shorten_memrefs
81
82 bool
83 pass_shorten_memrefs::get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend)
84 {
85 /* Whether it's sign/zero extended. */
86 if (GET_CODE (mem) == ZERO_EXTEND || GET_CODE (mem) == SIGN_EXTEND)
87 {
88 *extend = true;
89 mem = XEXP (mem, 0);
90 }
91
92 if (!MEM_P (mem) || GET_MODE (mem) != SImode)
93 return false;
94 *addr = XEXP (mem, 0);
95 return GET_CODE (*addr) == PLUS && REG_P (XEXP (*addr, 0));
96 }
97
98 /* Count how many times each regno is referenced as base address for a memory
99 access. */
100
101 pass_shorten_memrefs::regno_map *
102 pass_shorten_memrefs::analyze (basic_block bb)
103 {
104 regno_map *m = hash_map<regno_hash, int>::create_ggc (10);
105 rtx_insn *insn;
106
107 regstat_init_n_sets_and_refs ();
108
109 FOR_BB_INSNS (bb, insn)
110 {
111 if (!NONJUMP_INSN_P (insn))
112 continue;
113 rtx pat = PATTERN (insn);
114 if (GET_CODE (pat) != SET)
115 continue;
116 /* Analyze stores first then loads. */
117 for (int i = 0; i < 2; i++)
118 {
119 rtx mem = XEXP (pat, i);
120 rtx addr;
121 bool extend = false;
122 if (get_si_mem_base_reg (mem, &addr, &extend))
123 {
124 HOST_WIDE_INT regno = REGNO (XEXP (addr, 0));
125 /* Do not count store zero as these cannot be compressed. */
126 if (i == 0)
127 {
128 if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1))))
129 continue;
130 }
131 if (REG_N_REFS (regno) < 4)
132 continue;
133 m->get_or_insert (regno)++;
134 }
135 }
136 }
137 regstat_free_n_sets_and_refs ();
138
139 return m;
140 }
141
142 /* Convert BASE + LARGE_OFFSET to NEW_BASE + SMALL_OFFSET for each load/store
143 with a base reg referenced at least 4 times. */
144
145 void
146 pass_shorten_memrefs::transform (regno_map *m, basic_block bb)
147 {
148 rtx_insn *insn;
149 FOR_BB_INSNS (bb, insn)
150 {
151 if (!NONJUMP_INSN_P (insn))
152 continue;
153 rtx pat = PATTERN (insn);
154 if (GET_CODE (pat) != SET)
155 continue;
156 start_sequence ();
157 /* Transform stores first then loads. */
158 for (int i = 0; i < 2; i++)
159 {
160 rtx mem = XEXP (pat, i);
161 rtx addr;
162 bool extend = false;
163 if (get_si_mem_base_reg (mem, &addr, &extend))
164 {
165 HOST_WIDE_INT regno = REGNO (XEXP (addr, 0));
166 /* Do not transform store zero as these cannot be compressed. */
167 if (i == 0)
168 {
169 if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1))))
170 continue;
171 }
172 if (m->get_or_insert (regno) > 3)
173 {
174 if (extend)
175 {
176 addr
177 = targetm.legitimize_address (addr, addr,
178 GET_MODE (XEXP (mem, 0)));
179 XEXP (XEXP (pat, i), 0)
180 = replace_equiv_address (XEXP (mem, 0), addr);
181 }
182 else
183 {
184 addr = targetm.legitimize_address (addr, addr,
185 GET_MODE (mem));
186 XEXP (pat, i) = replace_equiv_address (mem, addr);
187 }
188 df_insn_rescan (insn);
189 }
190 }
191 }
192 rtx_insn *seq = get_insns ();
193 end_sequence ();
194 emit_insn_before (seq, insn);
195 }
196 }
197
198 unsigned int
199 pass_shorten_memrefs::execute (function *fn)
200 {
201 basic_block bb;
202
203 FOR_ALL_BB_FN (bb, fn)
204 {
205 regno_map *m;
206 if (optimize_bb_for_speed_p (bb))
207 continue;
208 m = analyze (bb);
209 transform (m, bb);
210 }
211
212 return 0;
213 }
214
215 } // anon namespace
216
217 rtl_opt_pass *
218 make_pass_shorten_memrefs (gcc::context *ctxt)
219 {
220 return new pass_shorten_memrefs (ctxt);
221 }