xref: /netbsd-src/external/gpl3/gcc/dist/gcc/config/rs6000/pcrel-opt.md (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1;; Machine description for the PCREL_OPT optimization.
2;; Copyright (C) 2020-2022 Free Software Foundation, Inc.
3;; Contributed by Michael Meissner (meissner@linux.ibm.com)
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;; Support for the PCREL_OPT optimization.  PCREL_OPT looks for instances where
22;; an external variable is used only once, either for reading or for writing.
23;;
24;; If we are optimizing a single read, normally the code would look like:
25;;
26;;	(set (reg:DI <ptr>)
27;;	     (symbol_ref:DI "<extern_addr>"))	# <data> is currently dead
28;;
29;;		...	# insns do not need to be adjacent
30;;
31;;	(set (reg:SI <data>)
32;;	     (mem:SI (reg:DI <xxx>)))		# <ptr> dies with this insn
33;;
34;; We transform this into:
35;;
36;;	(parallel [(set (reg:DI <ptr>)
37;;			(unspec:SI [(symbol_ref:DI <extern_addr>)
38;;				    (const_int <marker>)]
39;;				   UNSPEC_PCREL_OPT_LD_ADDR))
40;;		   (set (reg:DI <data>)
41;;			(unspec:DI [(const_int 0)]
42;;				   UNSPEC_PCREL_OPT_LD_DATA))])
43;;
44;;	...
45;;
46;;	(parallel [(set (reg:SI <data>)
47;;		   (unspec:SI [(mem:SI (reg:DI <ptr>))
48;;			       (reg:DI <data>)
49;;			       (const_int <marker>)]
50;;			      UNSPEC_PCREL_OPT_LD_RELOC))
51;;		   (clobber (reg:DI <ptr>))])
52;;
53;; The marker is an integer constant that links the load of the external
54;; address to the load of the actual variable.
55;;
56;; In the first insn, we set both the address of the external variable, and
57;; mark that the variable being loaded both are created in that insn, and are
58;; consumed in the second insn.	 It doesn't matter what mode the register that
59;; we will ultimately do the load into, so we use DImode.  We just need to mark
60;; that both registers may be set in the first insn, and will be used in the
61;; second insn.
62;;
63;; Since we use UNSPEC's and link both the register holding the external
64;; address and the value being loaded, it should prevent other passes from
65;; modifying it.
66;;
67;; If the register being loaded is the same as the base register, we use an
68;; alternate form of the insns.
69;;
70;;	(set (reg:DI <data_ptr>)
71;;	     (unspec:DI [(symbol_ref:DI <extern_addr>)
72;;			 (const_int <marker>)]
73;;			UNSPEC_PCREL_OPT_LD_SAME_REG))
74;;
75;;	...
76;;
77;;	(parallel [(set (reg:SI <data>)
78;;		   (unspec:SI [(mem:SI (reg:DI <ptr>))
79;;			       (reg:DI <data>)
80;;			       (const_int <marker>)]
81;;			      UNSPEC_PCREL_OPT_LD_RELOC))
82;;		   (clobber (reg:DI <ptr>))])
83
84(define_c_enum "unspec"
85  [UNSPEC_PCREL_OPT_LD_ADDR
86   UNSPEC_PCREL_OPT_LD_DATA
87   UNSPEC_PCREL_OPT_LD_SAME_REG
88   UNSPEC_PCREL_OPT_LD_RELOC
89   UNSPEC_PCREL_OPT_ST_ADDR
90   UNSPEC_PCREL_OPT_ST_RELOC])
91
92;; Modes that are supported for PCREL_OPT
93(define_mode_iterator PCRELOPT [QI HI SI DI TI SF DF KF
94			  V1TI V2DI V4SI V8HI V16QI V2DF V4SF
95			  (TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")])
96
97;; Vector modes for PCREL_OPT
98(define_mode_iterator PCRELOPT_VECT [TI KF V1TI V2DI V4SI V8HI V16QI V2DF V4SF
99			       (TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")])
100
101;; Insn for loading the external address, where the register being loaded is not
102;; the same as the register being loaded with the data.
103(define_insn "pcrel_opt_ld_addr"
104  [(set (match_operand:DI 0 "base_reg_operand" "=&b,&b")
105	(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
106		    (match_operand 2 "const_int_operand" "n,n")]
107		   UNSPEC_PCREL_OPT_LD_ADDR))
108   (set (match_operand:DI 3 "gpc_reg_operand" "=r,wa")
109	(unspec:DI [(const_int 0)]
110		   UNSPEC_PCREL_OPT_LD_DATA))]
111  "TARGET_PCREL_OPT
112   && reg_or_subregno (operands[0]) != reg_or_subregno (operands[3])"
113  "ld %0,%a1\n.Lpcrel%2:"
114  [(set_attr "prefixed" "yes")
115   (set_attr "type" "load")
116   (set_attr "loads_external_address" "yes")])
117
118;; Alternate form of loading up the external address that is the same register
119;; as the final load.
120(define_insn "pcrel_opt_ld_addr_same_reg"
121  [(set (match_operand:DI 0 "base_reg_operand" "=b")
122	(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
123		    (match_operand 2 "const_int_operand" "n")]
124		   UNSPEC_PCREL_OPT_LD_SAME_REG))]
125  "TARGET_PCREL_OPT"
126  "ld %0,%a1\n.Lpcrel%2:"
127  [(set_attr "prefixed" "yes")
128   (set_attr "type" "load")
129   (set_attr "loads_external_address" "yes")])
130
131;; PCREL_OPT modes that are optimized for loading or storing GPRs.
132(define_mode_iterator PCRELOPT_GPR [QI HI SI DI SF DF])
133
134(define_mode_attr PCRELOPT_GPR_LD [(QI "lbz")
135			     (HI "lhz")
136			     (SI "lwz")
137			     (SF "lwz")
138			     (DI "ld")
139			     (DF "ld")])
140
141;; PCREL_OPT load operation of GPRs.  Operand 4 (the register used to hold the
142;; address of the external symbol) is SCRATCH if the same register is used for
143;; the normal load.
144(define_insn "*pcrel_opt_ld<mode>_gpr"
145  [(parallel [(set (match_operand:PCRELOPT_GPR 0 "int_reg_operand" "+r")
146		   (unspec:PCRELOPT_GPR [
147		     (match_operand:PCRELOPT_GPR 1 "d_form_memory" "m")
148		     (match_operand:DI 2 "int_reg_operand" "0")
149		     (match_operand 3 "const_int_operand" "n")]
150		    UNSPEC_PCREL_OPT_LD_RELOC))
151	      (clobber (match_scratch:DI 4 "=bX"))])]
152  "TARGET_PCREL_OPT
153   && (GET_CODE (operands[4]) == SCRATCH
154       || reg_mentioned_p (operands[4], operands[1]))"
155{
156  output_pcrel_opt_reloc (operands[3]);
157  return "<PCRELOPT_GPR_LD> %0,%1";
158}
159  [(set_attr "type" "load")])
160
161;; PCREL_OPT load with sign/zero extension
162(define_insn "*pcrel_opt_ldsi_<u><mode>_gpr"
163  [(set (match_operand:EXTSI 0 "int_reg_operand" "+r")
164	(any_extend:EXTSI
165	 (unspec:SI [(match_operand:SI 1 "d_form_memory" "m")
166		     (match_operand:DI 2 "int_reg_operand" "0")
167		     (match_operand 3 "const_int_operand" "n")]
168		     UNSPEC_PCREL_OPT_LD_RELOC)))
169   (clobber (match_scratch:DI 4 "=bX"))]
170  "TARGET_PCREL_OPT"
171{
172  output_pcrel_opt_reloc (operands[3]);
173  return "lw<az> %0,%1";
174}
175  [(set_attr "type" "load")])
176
177(define_insn "*pcrel_opt_ldhi_<u><mode>_gpr"
178  [(set (match_operand:EXTHI 0 "int_reg_operand" "+r")
179	(any_extend:EXTHI
180	 (unspec:HI [(match_operand:HI 1 "d_form_memory" "m")
181		     (match_operand:DI 2 "int_reg_operand" "0")
182		     (match_operand 3 "const_int_operand" "n")]
183		     UNSPEC_PCREL_OPT_LD_RELOC)))
184   (clobber (match_scratch:DI 4 "=bX"))]
185  "TARGET_PCREL_OPT"
186{
187  output_pcrel_opt_reloc (operands[3]);
188  return "lh<az> %0,%1";
189}
190  [(set_attr "type" "load")])
191
192(define_insn "*pcrel_opt_ldqi_u<mode>_gpr"
193  [(set (match_operand:EXTQI 0 "int_reg_operand" "+r")
194	(zero_extend:EXTQI
195	 (unspec:QI [(match_operand:QI 1 "d_form_memory" "m")
196		     (match_operand:DI 2 "int_reg_operand" "0")
197		     (match_operand 3 "const_int_operand" "n")]
198		     UNSPEC_PCREL_OPT_LD_RELOC)))
199   (clobber (match_scratch:DI 4 "=bX"))]
200  "TARGET_PCREL_OPT"
201{
202  output_pcrel_opt_reloc (operands[3]);
203  return "lbz %0,%1";
204}
205  [(set_attr "type" "load")])
206
207;; Scalar types that can be optimized by loading them into floating point
208;; or Altivec registers.
209(define_mode_iterator PCRELOPT_FP [DI DF SF])
210
211;; Load instructions to load up scalar floating point or 64-bit integer values
212;; into floating point registers or Altivec registers.
213(define_mode_attr PCRELOPT_FPR_LD [(DI "lfd") (DF "lfd") (SF "lfs")])
214(define_mode_attr PCRELOPT_VMX_LD [(DI "lxsd") (DF "lxsd") (SF "lxssp")])
215
216;; PCREL_OPT load operation of scalar DF/DI/SF into vector registers.
217(define_insn "*pcrel_opt_ld<mode>_vsx"
218  [(set (match_operand:PCRELOPT_FP 0 "vsx_register_operand" "+d,v")
219	(unspec:PCRELOPT_FP [(match_operand:PCRELOPT_FP 1 "d_form_memory" "m,m")
220		       (match_operand:DI 2 "vsx_register_operand" "0,0")
221		       (match_operand 3 "const_int_operand" "n,n")]
222		       UNSPEC_PCREL_OPT_LD_RELOC))
223   (clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))]
224  "TARGET_PCREL_OPT"
225{
226  output_pcrel_opt_reloc (operands[3]);
227  return which_alternative ? "<PCRELOPT_VMX_LD> %0,%1"
228			 : "<PCRELOPT_FPR_LD> %0,%1";
229}
230  [(set_attr "type" "fpload")])
231
232;; PCREL_OPT optimization extending SFmode to DFmode via a load.
233(define_insn "*pcrel_opt_ldsf_df"
234  [(set (match_operand:DF 0 "vsx_register_operand" "+d,v")
235	(float_extend:DF
236	 (unspec:SF [(match_operand:SF 1 "d_form_memory" "m,m")
237		     (match_operand:DI 2 "vsx_register_operand" "0,0")
238		     (match_operand 3 "const_int_operand" "n,n")]
239		    UNSPEC_PCREL_OPT_LD_RELOC)))
240   (clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))]
241  "TARGET_PCREL_OPT"
242{
243  output_pcrel_opt_reloc (operands[3]);
244  return which_alternative ? "lxssp %0,%1" : "lfs %0,%1";
245}
246  [(set_attr "type" "fpload")])
247
248;; PCREL_OPT load operation of vector/float128 types into vector registers.
249(define_insn "*pcrel_opt_ld<mode>"
250  [(set (match_operand:PCRELOPT_VECT 0 "vsx_register_operand" "+wa")
251	(unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "d_form_memory" "m")
252			 (match_operand:DI 2 "vsx_register_operand" "0")
253			 (match_operand 3 "const_int_operand" "n")]
254			UNSPEC_PCREL_OPT_LD_RELOC))
255   (clobber (match_operand:DI 4 "base_reg_operand" "=b"))]
256  "TARGET_PCREL_OPT"
257{
258  output_pcrel_opt_reloc (operands[3]);
259  return "lxv %x0,%1";
260}
261  [(set_attr "type" "vecload")])
262
263
264;; PCREL_OPT optimization for stores.  We need to put the label after the PLD
265;; instruction, because the assembler might insert a NOP before the PLD for
266;; alignment.
267;;
268;; If we are optimizing a single write, normally the code would look like:
269;;
270;;	(set (reg:DI <ptr>)
271;;	     (symbol_ref:DI "<extern_addr>"))	# <data> must be live here
272;;
273;;	    ...		     # insns do not need to be adjacent
274;;
275;;	(set (mem:SI (reg:DI <xxx>))
276;;	     (reg:SI <data>))			# <ptr> dies with this insn
277;;
278;; We optimize this to be:
279;;
280;;	(parallel [(set (reg:DI <ptr>)
281;;			(unspec:DI [(symbol_ref:DI "<extern_addr>")
282;;				    (const_int <marker>)]
283;;				   UNSPEC_PCREL_OPT_ST_ADDR))
284;;		   (use (reg:<MODE> <data>))])
285;;
286;;	    ...		     # insns do not need to be adjacent
287;;
288;;	(parallel [(set (mem:<MODE> (reg:DI <ptr>))
289;;			(unspec:<MODE> [(reg:<MODE> <data>)
290;;					(const_int <marker>)]
291;;				       UNSPEC_PCREL_OPT_ST_RELOC))
292;;		   (clobber (reg:DI <ptr>))])
293
294(define_insn "*pcrel_opt_st_addr<mode>"
295  [(set (match_operand:DI 0 "gpc_reg_operand" "=b")
296	(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
297		    (match_operand 2 "const_int_operand" "n")]
298		UNSPEC_PCREL_OPT_ST_ADDR))
299   (use (match_operand:PCRELOPT 3 "gpc_reg_operand" "rwa"))]
300  "TARGET_PCREL_OPT"
301  "ld %0,%a1\n.Lpcrel%2:"
302  [(set_attr "prefixed" "yes")
303   (set_attr "type" "load")
304   (set_attr "loads_external_address" "yes")])
305
306;; PCREL_OPT stores.
307(define_insn "*pcrel_opt_st<mode>"
308  [(set (match_operand:QHSI 0 "d_form_memory" "=m")
309	(unspec:QHSI [(match_operand:QHSI 1 "gpc_reg_operand" "r")
310		      (match_operand 2 "const_int_operand" "n")]
311		     UNSPEC_PCREL_OPT_ST_RELOC))
312   (clobber (match_operand:DI 3 "base_reg_operand" "=b"))]
313  "TARGET_PCREL_OPT"
314{
315  output_pcrel_opt_reloc (operands[2]);
316  return "st<wd> %1,%0";
317}
318  [(set_attr "type" "store")])
319
320(define_insn "*pcrel_opt_stdi"
321  [(set (match_operand:DI 0 "d_form_memory" "=m,m,m")
322	(unspec:DI [(match_operand:DI 1 "gpc_reg_operand" "r,d,v")
323		    (match_operand 2 "const_int_operand" "n,n,n")]
324		   UNSPEC_PCREL_OPT_ST_RELOC))
325   (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
326  "TARGET_PCREL_OPT && TARGET_POWERPC64"
327{
328  output_pcrel_opt_reloc (operands[2]);
329  switch (which_alternative)
330    {
331    case 0:
332      return "std %1,%0";
333    case 1:
334      return "stfd %1,%0";
335    case 2:
336      return "stxsd %1,%0";
337    default:
338      gcc_unreachable ();
339    }
340}
341  [(set_attr "type" "store,fpstore,fpstore")])
342
343(define_insn "*pcrel_opt_stsf"
344  [(set (match_operand:SF 0 "d_form_memory" "=m,m,m")
345	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "d,v,r")
346		    (match_operand 2 "const_int_operand" "n,n,n")]
347		   UNSPEC_PCREL_OPT_ST_RELOC))
348   (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
349  "TARGET_PCREL_OPT"
350{
351  output_pcrel_opt_reloc (operands[2]);
352  switch (which_alternative)
353    {
354    case 0:
355      return "stfs %1,%0";
356    case 1:
357      return "stxssp %1,%0";
358    case 2:
359      return "stw %1,%0";
360    default:
361      gcc_unreachable ();
362    }
363}
364  [(set_attr "type" "fpstore,fpstore,store")])
365
366(define_insn "*pcrel_opt_stdf"
367  [(set (match_operand:DF 0 "d_form_memory" "=m,m,m")
368	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d,v,r")
369		    (match_operand 2 "const_int_operand" "n,n,n")]
370		   UNSPEC_PCREL_OPT_ST_RELOC))
371   (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
372  "TARGET_PCREL_OPT
373   && (TARGET_POWERPC64 || vsx_register_operand (operands[1], DFmode))"
374{
375  output_pcrel_opt_reloc (operands[2]);
376  switch (which_alternative)
377    {
378    case 0:
379      return "stfd %1,%0";
380    case 1:
381      return "stxsd %1,%0";
382    case 2:
383      return "std %1,%0";
384    default:
385      gcc_unreachable ();
386    }
387}
388  [(set_attr "type" "fpstore,fpstore,store")])
389
390(define_insn "*pcrel_opt_st<mode>"
391  [(set (match_operand:PCRELOPT_VECT 0 "d_form_memory" "=m")
392	(unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "gpc_reg_operand" "wa")
393		     (match_operand 2 "const_int_operand" "n")]
394		    UNSPEC_PCREL_OPT_ST_RELOC))
395   (clobber (match_operand:DI 3 "base_reg_operand" "=b"))]
396  "TARGET_PCREL_OPT"
397{
398  output_pcrel_opt_reloc (operands[2]);
399  return "stxv %x1,%0";
400}
401  [(set_attr "type" "vecstore")])
402