1 /* Subroutines used support the pc-relative linker optimization.
2    Copyright (C) 2020-2021 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 it
7    under the terms of the GNU General Public License as published
8    by the Free Software Foundation; either version 3, or (at your
9    option) any later version.
10 
11    GCC is distributed in the hope that it will be useful, but WITHOUT
12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14    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 /* This file implements a RTL pass that looks for pc-relative loads of the
21    address of an external variable using the PCREL_GOT relocation and a single
22    load that uses that external address.  If that is found we create the
23    PCREL_OPT relocation to possibly convert:
24 
25 	pld addr_reg,var@pcrel@got
26 
27 	<possibly other insns that do not use 'addr_reg' or 'data_reg'>
28 
29 	lwz data_reg,0(addr_reg)
30 
31    into:
32 
33 	plwz data_reg,var@pcrel
34 
35 	<possibly other insns that do not use 'addr_reg' or 'data_reg'>
36 
37 	nop
38 
39    Of course it would be nice to be able to put the plwz in this example in
40    place of the lwz but the linker cannot easily replace a 4-byte instruction
41    with an 8-byte one.
42 
43    If the variable is not defined in the main program or the code using it is
44    not in the main program, the linker puts the address in the .got section and
45    generates:
46 
47 		.section .got
48 	.Lvar_got:
49 		.dword var
50 
51    At the point where it is referenced, we have:
52 
53 		.section .text
54 		pld addr_reg,.Lvar_got@pcrel
55 
56 		<possibly other insns that do not use 'addr_reg' or 'data_reg'>
57 
58 		lwz data_reg,0(addr_reg)
59 
60    We look for a single usage in the basic block where this external
61    address is loaded, and convert it to a PCREL_OPT relocation so the
62    linker can convert it to a single plwz in this case.  Multiple uses
63    or references in another basic block will force us to not use the
64    PCREL_OPT relocation.
65 
66    We also optimize stores to the address of an external variable using the
67    PCREL_GOT relocation and a single store that uses that external address.  If
68    that is found we create the PCREL_OPT relocation to possibly convert:
69 
70 	pld addr_reg,var@pcrel@got
71 
72 	<possibly other insns that do not use 'addr_reg' or 'data_reg'>
73 
74 	stw data_reg,0(addr_reg)
75 
76    into:
77 
78 	pstw data_reg,var@pcrel
79 
80 	<possibly other insns that do not use 'addr_reg' or 'data_reg'>
81 
82 	nop
83 
84    If the variable is not defined in the main program or the code using it is
85    not in the main program, the linker puts the address in the .got section and
86    generates:
87 
88 		.section .got
89 	.Lvar_got:
90 		.dword var
91 
92    And at our point of reference we have:
93 
94 		.section .text
95 		pld addr_reg,.Lvar_got@pcrel
96 
97 		<possibly other insns that do not use 'addr_reg' or 'data_reg'>
98 
99 		stw data_reg,0(addr_reg)
100 
101    We only look for a single usage in the basic block where the external
102    address is loaded.  Multiple uses or references in another basic block will
103    force us to not use the PCREL_OPT relocation.  */
104 
105 #define IN_TARGET_CODE 1
106 
107 #include "config.h"
108 #include "system.h"
109 #include "coretypes.h"
110 #include "backend.h"
111 #include "rtl.h"
112 #include "tree.h"
113 #include "memmodel.h"
114 #include "expmed.h"
115 #include "optabs.h"
116 #include "recog.h"
117 #include "df.h"
118 #include "tm_p.h"
119 #include "ira.h"
120 #include "print-tree.h"
121 #include "varasm.h"
122 #include "explow.h"
123 #include "expr.h"
124 #include "output.h"
125 #include "tree-pass.h"
126 #include "rtx-vector-builder.h"
127 #include "print-rtl.h"
128 #include "insn-attr.h"
129 #include "insn-codes.h"
130 
131 /* Various counters.  */
132 static struct {
133   unsigned long extern_addrs;
134   unsigned long loads;
135   unsigned long adjacent_loads;
136   unsigned long failed_loads;
137   unsigned long stores;
138   unsigned long adjacent_stores;
139   unsigned long failed_stores;
140 } counters;
141 
142 /* Unique integer that is appended to .Lpcrel to make a pcrel_opt label. */
143 static unsigned int pcrel_opt_next_num;
144 
145 
146 /* Optimize a PC-relative load address to be used in a load. Before it calls
147    this function, pcrel_opt_address () uses DF to make sure that it is safe
148    to do the PCREL_OPT optimization on these insns.
149 
150    Convert insns of the form:
151 
152 	(set (reg:DI addr)
153 	     (symbol_ref:DI "ext_symbol"))
154 
155 	...
156 
157 	(set (reg:<MODE> value)
158 	     (mem:<MODE> (reg:DI addr)))
159 
160    into:
161 
162 	(parallel [(set (reg:DI addr)
163 			(unspec:<MODE> [(symbol_ref:DI "ext_symbol")
164 					(const_int label_num)]
165 				       UNSPEC_PCREL_OPT_LD_ADDR))
166 		   (set (reg:DI data)
167 			(unspec:DI [(const_int 0)]
168 				   UNSPEC_PCREL_OPT_LD_DATA))])
169 
170 	...
171 
172 	(parallel [(set (reg:<MODE>)
173 			(unspec:<MODE> [(mem:<MODE> (reg:DI addr))
174 					(reg:DI data)
175 					(const_int label_num)]
176 				       UNSPEC_PCREL_OPT_LD_RELOC))
177 		   (clobber (reg:DI addr))])
178 
179    Because PCREL_OPT will move the actual location of the load from the second
180    insn to the first, we need to have the register for the load data be live
181    starting at the first insn.
182 
183    If the destination register for the data being loaded is the same register
184    used to hold the extern address, we generate this insn instead:
185 
186 	(set (reg:DI data)
187 	     (unspec:DI [(symbol_ref:DI "ext_symbol")
188 			 (const_int label_num)]
189 			UNSPEC_PCREL_OPT_LD_SAME_REG))
190 
191    In the first insn, we set both the address of the external variable, and mark
192    that the variable being loaded both are created in that insn, and are
193    consumed in the second insn.  The mode used in the first insn for the data
194    register that will be loaded in the second insn doesn't matter in the end so
195    we use DImode.  We just need to mark that both registers may be set in the
196    first insn, and will be used in the second insn.
197 
198    The UNSPEC_PCREL_OPT_LD_ADDR insn will generate the load address plus
199    a definition of a label (.Lpcrel<n>), while the UNSPEC_PCREL_OPT_LD_RELOC
200    insn will generate the .reloc to tell the linker to tie the load address and
201    load using that address together.
202 
203 	pld b,ext_symbol@got@pcrel
204    .Lpcrel1:
205 
206 	...
207 
208 	.reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
209 	lwz r,0(b)
210 
211    If ext_symbol is defined in another object file in the main program and we
212    are linking the main program, the linker will convert the above instructions
213    to:
214 
215 	plwz r,ext_symbol@got@pcrel
216 
217 	...
218 
219 	nop
220 
221    ADDR_INSN is the insn that is loading the address.
222    LOAD_INSN is the insn that uses the address to load the actual data.  */
223 
224 static void
pcrel_opt_load(rtx_insn * addr_insn,rtx_insn * load_insn)225 pcrel_opt_load (rtx_insn *addr_insn, rtx_insn *load_insn)
226 {
227   rtx addr_set = PATTERN (addr_insn);
228   gcc_assert (GET_CODE (addr_set) == SET);
229 
230   rtx addr_reg = SET_DEST (addr_set);
231   gcc_assert (base_reg_operand (addr_reg, Pmode));
232 
233   rtx addr_symbol = SET_SRC (addr_set);
234   gcc_assert (pcrel_external_address (addr_symbol, Pmode));
235 
236   rtx load_set = PATTERN (load_insn);
237   gcc_assert (GET_CODE (load_set) == SET);
238 
239   /* Make sure there are no references to the register being loaded
240      between the two insns.  */
241   rtx reg = SET_DEST (load_set);
242   if (reg_used_between_p (reg, addr_insn, load_insn)
243       || reg_set_between_p (reg, addr_insn, load_insn))
244     return;
245 
246   rtx mem = SET_SRC (load_set);
247   machine_mode reg_mode = GET_MODE (reg);
248   machine_mode mem_mode = GET_MODE (mem);
249   rtx mem_inner = mem;
250   unsigned int reg_regno = reg_or_subregno (reg);
251 
252   /* Handle the fact that LWA is a DS format instruction, but LWZ is a D format
253      instruction.  If the mem load is a signed SImode (i.e. LWA would be used)
254      we set mem_mode to DImode so that pcrel_opt_valid_mem_p() will check that
255      the address will work for a DS-form instruction. If it won't work, we skip
256      the optimization.  The float loads are all indexed so there are no problems
257      there.  */
258 
259   if (GET_CODE (mem) == SIGN_EXTEND && GET_MODE (XEXP (mem, 0)) == SImode)
260     {
261       if (!INT_REGNO_P (reg_regno))
262 	return;
263 
264       mem_inner = XEXP (mem, 0);
265       mem_mode = DImode;
266     }
267 
268   else if (GET_CODE (mem) == SIGN_EXTEND
269 	   || GET_CODE (mem) == ZERO_EXTEND
270 	   || GET_CODE (mem) == FLOAT_EXTEND)
271     {
272       mem_inner = XEXP (mem, 0);
273       mem_mode = GET_MODE (mem_inner);
274     }
275 
276   if (!MEM_P (mem_inner))
277     return;
278 
279   /* Can we do PCREL_OPT for this reference?  */
280   if (!pcrel_opt_valid_mem_p (reg, mem_mode, mem_inner))
281     return;
282 
283   /* Allocate a new PC-relative label, and update the load external address
284      insn.
285 
286      If the register being loaded is different from the address register, we
287      need to indicate both registers are set at the load of the address.
288 
289 	(parallel [(set (reg load)
290 			(unspec [(symbol_ref addr_symbol)
291 				 (const_int label_num)]
292 				UNSPEC_PCREL_OPT_LD_ADDR))
293 		   (set (reg addr)
294 			(unspec [(const_int 0)]
295 				UNSPEC_PCREL_OPT_LD_DATA))])
296 
297      If the register being loaded is the same as the address register, we use
298      an alternate form:
299 
300 	(set (reg load)
301 	     (unspec [(symbol_ref addr_symbol)
302 		      (const_int label_num)]
303 		     UNSPEC_PCREL_OPT_LD_SAME_REG))  */
304   unsigned int addr_regno = reg_or_subregno (addr_reg);
305   rtx label_num = GEN_INT (++pcrel_opt_next_num);
306   rtx reg_di = gen_rtx_REG (DImode, reg_regno);
307   rtx addr_pattern;
308 
309   /* Create the load address, either using the pattern with an explicit clobber
310      if the address register is not the same as the register being loaded, or
311      using the pattern that requires the address register to be the address
312      loaded.  */
313   if (addr_regno != reg_regno)
314     addr_pattern = gen_pcrel_opt_ld_addr (addr_reg, addr_symbol, label_num,
315 					  reg_di);
316   else
317     addr_pattern = gen_pcrel_opt_ld_addr_same_reg (addr_reg, addr_symbol,
318 						   label_num);
319 
320   validate_change (addr_insn, &PATTERN (addr_insn), addr_pattern, false);
321 
322   /* Update the load insn.  If the mem had a sign/zero/float extend, add that
323      also after doing the UNSPEC.  Add an explicit clobber of the external
324      address register just to make it clear that the address register dies.
325 
326 	(parallel [(set (reg:<MODE> data)
327 			(unspec:<MODE> [(mem (addr_reg)
328 					(reg:DI data)
329 					(const_int label_num)]
330 				       UNSPEC_PCREL_OPT_LD_RELOC))
331 		   (clobber (reg:DI addr_reg))])  */
332   rtvec v_load = gen_rtvec (3, mem_inner, reg_di, label_num);
333   rtx new_load = gen_rtx_UNSPEC (GET_MODE (mem_inner), v_load,
334 				 UNSPEC_PCREL_OPT_LD_RELOC);
335 
336   if (GET_CODE (mem) != GET_CODE (mem_inner))
337     new_load = gen_rtx_fmt_e (GET_CODE (mem), reg_mode, new_load);
338 
339   rtx new_load_set = gen_rtx_SET (reg, new_load);
340   rtx load_clobber = gen_rtx_CLOBBER (VOIDmode,
341 				      (addr_regno == reg_regno
342 				       ? gen_rtx_SCRATCH (Pmode)
343 				       : addr_reg));
344   rtx new_load_pattern
345     = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, new_load_set, load_clobber));
346 
347   validate_change (load_insn, &PATTERN (load_insn), new_load_pattern, false);
348 
349   /* Attempt to apply the changes:  */
350   if (!apply_change_group ())
351     {
352       /* PCREL_OPT load optimization did not succeed.  */
353       counters.failed_loads++;
354       if (dump_file)
355 	fprintf (dump_file,
356 		 "PCREL_OPT load failed (addr insn = %d, use insn = %d).\n",
357 		 INSN_UID (addr_insn),
358 		 INSN_UID (load_insn));
359       return;
360     }
361 
362   /* PCREL_OPT load optimization succeeded.  */
363   counters.loads++;
364   if (next_nonnote_insn (addr_insn) == load_insn)
365     counters.adjacent_loads++;
366 
367   if (dump_file)
368     fprintf (dump_file,
369 	     "PCREL_OPT load (addr insn = %d, use insn = %d).\n",
370 	     INSN_UID (addr_insn),
371 	     INSN_UID (load_insn));
372 
373   /* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
374      after we have made changes to the insns.  */
375   df_analyze ();
376 
377 }
378 
379 /* Optimize a PC-relative load address to be used in a store. Before calling
380    this function, pcrel_opt_address () uses DF to make sure it is safe to do
381    the PCREL_OPT optimization.
382 
383    Convert insns of the form:
384 
385 	(set (reg:DI addr)
386 	     (symbol_ref:DI "ext_symbol"))
387 
388 	...
389 
390 	(set (mem:<MODE> (reg:DI addr))
391 	     (reg:<MODE> value))
392 
393    into:
394 
395 	(parallel [(set (reg:DI addr)
396 			(unspec:DI [(symbol_ref:DI "ext_symbol")
397 				    (const_int label_num)]
398 				  UNSPEC_PCREL_OPT_ST_ADDR))
399 		  (use (reg:<MODE> value))])
400 
401 	...
402 
403 	(parallel [(set (mem:<MODE> (reg:DI addr))
404 			(unspec:<MODE> [(reg:<MODE>)
405 					(const_int label_num)]
406 				       UNSPEC_PCREL_OPT_ST_RELOC))
407 		   (clobber (reg:DI addr))])
408 
409    The UNSPEC_PCREL_OPT_ST_ADDR insn will generate the load address plus a
410    definition of a label (.Lpcrel<n>), while the UNSPEC_PCREL_OPT_ST_RELOC insn
411    will generate the .reloc to tell the linker to tie the load address and load
412    using that address together.
413 
414 	pld b,ext_symbol@got@pcrel
415    .Lpcrel1:
416 
417 	...
418 
419 	.reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
420 	stw r,0(b)
421 
422    If ext_symbol is defined in another object file in the main program and we
423    are linking the main program, the linker will convert the above instructions
424    to:
425 
426 	pstwz r,ext_symbol@got@pcrel
427 
428 	...
429 
430 	nop  */
431 
432 static void
pcrel_opt_store(rtx_insn * addr_insn,rtx_insn * store_insn)433 pcrel_opt_store (rtx_insn *addr_insn,		/* insn loading address.  */
434 		 rtx_insn *store_insn)		/* insn using address.  */
435 {
436   rtx addr_old_set = PATTERN (addr_insn);
437   gcc_assert (GET_CODE (addr_old_set) == SET);
438 
439   rtx addr_reg = SET_DEST (addr_old_set);
440   gcc_assert (base_reg_operand (addr_reg, Pmode));
441 
442   rtx addr_symbol = SET_SRC (addr_old_set);
443   gcc_assert (pcrel_external_address (addr_symbol, Pmode));
444 
445   rtx store_set = PATTERN (store_insn);
446   gcc_assert (GET_CODE (store_set) == SET);
447 
448   rtx mem = SET_DEST (store_set);
449   if (!MEM_P (mem))
450     return;
451 
452   machine_mode mem_mode = GET_MODE (mem);
453   rtx reg = SET_SRC (store_set);
454 
455   /* Don't allow storing the address of the external variable.  */
456   if (reg_or_subregno (reg) == reg_or_subregno (addr_reg))
457     return;
458 
459   /* Can we do PCREL_OPT for this reference?  */
460   if (!pcrel_opt_valid_mem_p (reg, mem_mode, mem))
461     return;
462 
463   /* Allocate a new PC-relative label, and update the load address insn.
464 
465 	(parallel [(set (reg addr)
466 		       (unspec [(symbol_ref symbol)
467 				 (const_int label_num)]
468 				UNSPEC_PCREL_OPT_ST_ADDR))
469 		  (use (reg store))])
470   */
471   rtx label_num = GEN_INT (++pcrel_opt_next_num);
472   rtvec v_addr = gen_rtvec (2, addr_symbol, label_num);
473   rtx addr_unspec = gen_rtx_UNSPEC (Pmode, v_addr,
474 				   UNSPEC_PCREL_OPT_ST_ADDR);
475   rtx addr_new_set = gen_rtx_SET (addr_reg, addr_unspec);
476   rtx addr_use = gen_rtx_USE (VOIDmode, reg);
477   rtx addr_new_pattern
478     = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, addr_new_set, addr_use));
479 
480   validate_change (addr_insn, &PATTERN (addr_insn), addr_new_pattern, false);
481 
482   /* Update the store insn.  Add an explicit clobber of the external address
483      register just to be sure there are no additional uses of the address
484      register.
485 
486 	(parallel [(set (mem (addr_reg)
487 			(unspec:<MODE> [(reg)
488 					(const_int label_num)]
489 				       UNSPEC_PCREL_OPT_ST_RELOC))
490 		  (clobber (reg:DI addr_reg))])  */
491   rtvec v_store = gen_rtvec (2, reg, label_num);
492   rtx new_store = gen_rtx_UNSPEC (mem_mode, v_store,
493 				  UNSPEC_PCREL_OPT_ST_RELOC);
494 
495   rtx new_store_set = gen_rtx_SET (mem, new_store);
496   rtx store_clobber = gen_rtx_CLOBBER (VOIDmode, addr_reg);
497   rtx new_store_pattern
498     = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, new_store_set, store_clobber));
499 
500   validate_change (store_insn, &PATTERN (store_insn), new_store_pattern, false);
501 
502   /* Attempt to apply the changes:  */
503   if (!apply_change_group ())
504     {
505       /* PCREL_OPT store failed.  */
506       counters.failed_stores++;
507       if (dump_file)
508 	fprintf (dump_file,
509 		 "PCREL_OPT store failed (addr insn = %d, use insn = %d).\n",
510 		 INSN_UID (addr_insn),
511 		 INSN_UID (store_insn));
512       return;
513     }
514 
515   /* PCREL_OPT store succeeded.  */
516   counters.stores++;
517   if (next_nonnote_insn (addr_insn) == store_insn)
518     counters.adjacent_stores++;
519 
520   if (dump_file)
521     fprintf (dump_file,
522 	     "PCREL_OPT store (addr insn = %d, use insn = %d).\n",
523 	     INSN_UID (addr_insn),
524 	     INSN_UID (store_insn));
525 
526   /* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
527      after we have made changes to the insns.  */
528   df_analyze();
529 
530 }
531 
532 /* Return the register used as the base register of MEM, if the instruction has
533    a pc-relative form.  We look for BSWAP to rule out LFIWAX/LFIWZX/STFIWX, and
534    ROTATE/VEC_SELECT are RTX_EXTRA not RTX_UNARY which rules out lxvd2x. This
535    excludes instructions that do not have a pc-relative form.  */
536 
537 static rtx
get_mem_base_reg(rtx mem)538 get_mem_base_reg (rtx mem)
539 {
540   const char * fmt;
541 
542   while (!MEM_P (mem))
543     {
544       if (GET_RTX_CLASS (GET_CODE (mem)) != RTX_UNARY
545 	  || GET_CODE (mem) == BSWAP)
546 	return NULL_RTX;
547       fmt = GET_RTX_FORMAT (GET_CODE (mem));
548       if (fmt[0] != 'e')
549 	return NULL_RTX;
550       mem = XEXP (mem, 0);
551       if (mem == NULL_RTX )
552 	return NULL_RTX;
553     }
554 
555   if (!MEM_SIZE_KNOWN_P (mem))
556     return NULL_RTX;
557 
558   rtx addr_rtx = (XEXP (mem, 0));
559   if (GET_CODE (addr_rtx) == PRE_MODIFY)
560     addr_rtx = XEXP (addr_rtx, 1);
561 
562   while (GET_CODE (addr_rtx) == PLUS
563 	 && CONST_INT_P (XEXP (addr_rtx, 1)))
564     addr_rtx = XEXP (addr_rtx, 0);
565 
566   if (!REG_P (addr_rtx))
567     return NULL_RTX;
568 
569   return addr_rtx;
570 }
571 
572 /* Check whether INSN contains a reference to REGNO that will inhibit the
573    PCREL_OPT optimization.  If TYPE is a load or store instruction, return true
574    if there is a definition of REGNO.  If TYPE is a load instruction, then
575    return true of there is a use of REGNO.  */
576 
577 static bool
insn_references_regno_p(rtx_insn * insn,unsigned int regno,enum attr_type type)578 insn_references_regno_p (rtx_insn *insn, unsigned int regno,
579 		       enum attr_type type)
580 {
581   struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
582   df_ref ref;
583 
584   /* Return true if there is a definition of REGNO.  */
585   for (ref = DF_INSN_INFO_DEFS (insn_info); ref; ref = DF_REF_NEXT_LOC (ref))
586     if (DF_REF_REGNO (ref) == regno)
587       return true;
588 
589   /* If type is a load, return true if there is a use of REGNO.  */
590   if (type == TYPE_LOAD
591       || type == TYPE_FPLOAD
592       || type == TYPE_VECLOAD)
593     for (ref = DF_INSN_INFO_USES (insn_info); ref; ref = DF_REF_NEXT_LOC (ref))
594       if (DF_REF_REGNO (ref) == regno)
595 	return true;
596 
597   return false;
598 }
599 
600 /* Given an insn that loads up a base register with the address of an
601    external symbol, see if we can optimize it with the PCREL_OPT
602    optimization.
603 
604    DF is used to make sure that there is exactly one definition and one
605    non-debug use of the address register defined by the insn. The use insn must
606    be a non-prefix insn, and must also be in the same basic block as the address
607    insn.
608 
609    ADDR_INSN is the insn that loads the external symbol address.  */
610 
611 static void
pcrel_opt_address(rtx_insn * addr_insn)612 pcrel_opt_address (rtx_insn *addr_insn)
613 {
614   counters.extern_addrs++;
615 
616   /* Do some basic validation.  */
617   rtx addr_set = PATTERN (addr_insn);
618   if (GET_CODE (addr_set) != SET)
619     return;
620 
621   rtx addr_reg = SET_DEST (addr_set);
622   rtx addr_symbol = SET_SRC (addr_set);
623 
624   if (!base_reg_operand (addr_reg, Pmode)
625       || !pcrel_external_address (addr_symbol, Pmode))
626     return;
627 
628   /* The address register must have exactly one definition.  */
629   struct df_insn_info *insn_info = DF_INSN_INFO_GET (addr_insn);
630   if (!insn_info)
631     return;
632 
633   df_ref def = df_single_def (insn_info);
634   if (!def)
635     return;
636 
637   /* Make sure there is at least one use.  */
638   df_link *chain = DF_REF_CHAIN (def);
639   if (!chain || !chain->ref)
640     return;
641 
642   /* Get the insn of the possible load or store.  */
643   rtx_insn *use_insn = DF_REF_INSN (chain->ref);
644 
645   /* Ensure there are no other uses.  */
646   for (chain = chain->next; chain; chain = chain->next)
647     if (chain->ref && DF_REF_INSN_INFO (chain->ref))
648       {
649 	gcc_assert (DF_REF_INSN (chain->ref));
650 	if (NONDEBUG_INSN_P (DF_REF_INSN (chain->ref)))
651 	  return;
652       }
653 
654   /* The use instruction must be a single non-prefixed instruction.  */
655   if (get_attr_length (use_insn) != 4)
656     return;
657 
658   /* The address and the memory operation must be in the same basic block.  */
659   if (BLOCK_FOR_INSN (use_insn) != BLOCK_FOR_INSN (addr_insn))
660     return;
661 
662   /* If this isn't a simple SET, skip doing the optimization.  */
663   if (GET_CODE (PATTERN (use_insn)) != SET)
664     return;
665 
666   enum attr_type use_insn_type = get_attr_type (use_insn);
667   unsigned int use_regno;
668 
669   /* Make sure the use_insn is using addr_reg as its base register
670      for the load or store, and determine the regno for the register
671      used in the use_insn.  */
672   rtx use_dest, use_src;
673   switch (use_insn_type)
674     {
675     case TYPE_LOAD:
676     case TYPE_FPLOAD:
677     case TYPE_VECLOAD:
678       /* Make sure our address register is the same register used in the
679 	 base address of the load.  */
680       if (addr_reg != get_mem_base_reg (SET_SRC (PATTERN (use_insn))))
681 	return;
682       /* Make sure we are setting a register before we look at REGNO.  */
683       use_dest = SET_DEST (PATTERN (use_insn));
684       if (!register_operand (use_dest, GET_MODE (use_dest)))
685 	return;
686       use_regno = REGNO (use_dest);
687       break;
688     case TYPE_STORE:
689     case TYPE_FPSTORE:
690     case TYPE_VECSTORE:
691       /* Make sure our address register is the same register used in the
692 	 base address of the store.  */
693       if (addr_reg != get_mem_base_reg (SET_DEST (PATTERN (use_insn))))
694 	return;
695       /* Make sure this is a register before we look at REGNO.  */
696       use_src = SET_SRC (PATTERN (use_insn));
697       if (!register_operand (use_src, GET_MODE (use_src)))
698 	return;
699       use_regno = REGNO (use_src);
700       break;
701     default:
702       /* We can only optimize loads and stores.  Ignore everything else.  */
703       return;
704     }
705 
706   rtx_insn *insn;
707   for (insn = NEXT_INSN (addr_insn);
708        insn != use_insn;
709        insn = NEXT_INSN (insn))
710     {
711       /* If we see a call, do not do the PCREL_OPT optimization.  */
712       if (CALL_P (insn))
713 	return;
714 
715       /* Skip debug insns.  */
716       if (!NONDEBUG_INSN_P (insn))
717 	continue;
718 
719       /* See if it is a load or store.  */
720       if (GET_CODE (PATTERN (insn)) != USE
721 	  && GET_CODE (PATTERN (insn)) != CLOBBER)
722 	{
723 	  switch (get_attr_type (insn))
724 	    {
725 	    case TYPE_LOAD:
726 	      /* While load of the external address is a 'load' for scheduling
727 		 purposes, it should be safe to allow loading other external
728 		 addresses between the load of the external address we are
729 		 currently looking at and the load or store using that
730 		 address.  */
731 	      if (get_attr_loads_external_address (insn)
732 		  == LOADS_EXTERNAL_ADDRESS_YES)
733 		break;
734 	      /* fall through */
735 
736 	    case TYPE_FPLOAD:
737 	    case TYPE_VECLOAD:
738 	      /* Don't do the PCREL_OPT store optimization if there is a load
739 		 operation.  For example, the load might be trying to load the
740 		 value being stored in between getting the address and doing
741 		 the store.  */
742 	      if (use_insn_type == TYPE_STORE
743 		  || use_insn_type == TYPE_FPSTORE
744 		  || use_insn_type == TYPE_VECSTORE)
745 		return;
746 	      break;
747 
748 	    case TYPE_STORE:
749 	    case TYPE_FPSTORE:
750 	    case TYPE_VECSTORE:
751 	      /* Don't do the PCREL_OPT load optimization if there is a store
752 		 operation.  Perhaps the store might be to the global variable
753 		 through a pointer.  */
754 	      return;
755 
756 	    case TYPE_LOAD_L:
757 	    case TYPE_STORE_C:
758 	    case TYPE_HTM:
759 	    case TYPE_HTMSIMPLE:
760 	      /* Don't do the optimization through atomic operations.  */
761 	      return;
762 
763 	    default:
764 	      break;
765 	    }
766 	}
767 
768       /* Check for invalid references of the non-address register that is
769 	 used in the load or store instruction.  */
770       if (insn_references_regno_p (insn, use_regno, use_insn_type))
771 	return;
772     }
773 
774   /* Is this a load or a store?  */
775   switch (use_insn_type)
776     {
777     case TYPE_LOAD:
778     case TYPE_FPLOAD:
779     case TYPE_VECLOAD:
780       pcrel_opt_load (addr_insn, use_insn);
781       break;
782 
783     case TYPE_STORE:
784     case TYPE_FPSTORE:
785     case TYPE_VECSTORE:
786       pcrel_opt_store (addr_insn, use_insn);
787       break;
788 
789     default:
790       gcc_unreachable ();
791     }
792 }
793 
794 /* Optimize pcrel external variable references.  */
795 
796 static unsigned int
pcrel_opt_pass(function * fun)797 pcrel_opt_pass (function *fun)
798 {
799   basic_block bb;
800   rtx_insn *insn, *curr_insn = 0;
801 
802   memset (&counters, 0, sizeof (counters));
803 
804   /* Dataflow analysis for use-def chains.  However we have to specify both UD
805    and DU as otherwise when we make changes to insns for the PCREL_OPT there
806    will be dangling references.  */
807   df_set_flags (DF_RD_PRUNE_DEAD_DEFS);
808   df_chain_add_problem (DF_DU_CHAIN + DF_UD_CHAIN);
809   df_note_add_problem ();
810   df_analyze ();
811 
812   /* Set the defer flag as our pattern of operation will be to modify two insns,
813      then call df_analyze ().  */
814   df_set_flags (DF_DEFER_INSN_RESCAN | DF_LR_RUN_DCE);
815 
816   if (dump_file)
817     fprintf (dump_file, "\n");
818 
819   /* Look at each basic block to see if there is a load of an external
820      variable's external address, and a single load/store using that external
821      address.  */
822   FOR_ALL_BB_FN (bb, fun)
823     {
824       FOR_BB_INSNS_SAFE (bb, insn, curr_insn)
825 	{
826 	  if (NONJUMP_INSN_P (insn)
827 	      && single_set (insn)
828 	      && get_attr_loads_external_address (insn)
829 	      == LOADS_EXTERNAL_ADDRESS_YES)
830 	    pcrel_opt_address (insn);
831 	}
832     }
833 
834   if (dump_file)
835     {
836       fprintf (dump_file,
837 	       "\n# of loads of an address of an external symbol = %lu\n",
838 	       counters.extern_addrs);
839 
840       fprintf (dump_file, "# of PCREL_OPT loads = %lu (adjacent %lu)\n",
841 	       counters.loads, counters.adjacent_loads);
842 
843       if (counters.failed_loads)
844 	fprintf (dump_file, "# of failed PCREL_OPT loads = %lu\n",
845 		 counters.failed_loads);
846 
847       fprintf (dump_file, "# of PCREL_OPT stores = %lu (adjacent %lu)\n",
848 	       counters.stores, counters.adjacent_stores);
849 
850       if (counters.failed_stores)
851 	fprintf (dump_file, "# of failed PCREL_OPT stores = %lu\n",
852 		 counters.failed_stores);
853 
854       fprintf (dump_file, "\n");
855     }
856 
857   df_remove_problem (df_chain);
858   df_process_deferred_rescans ();
859   df_set_flags (DF_RD_PRUNE_DEAD_DEFS | DF_LR_RUN_DCE);
860   df_analyze ();
861   return 0;
862 }
863 
864 /* Optimize pc-relative references for the new PCREL_OPT pass.  */
865 const pass_data pass_data_pcrel_opt =
866 {
867   RTL_PASS,			/* type.  */
868   "pcrel_opt",			/* name.  */
869   OPTGROUP_NONE,		/* optinfo_flags.  */
870   TV_NONE,			/* tv_id.  */
871   0,				/* properties_required.  */
872   0,				/* properties_provided.  */
873   0,				/* properties_destroyed.  */
874   0,				/* todo_flags_start.  */
875   TODO_df_finish,		/* todo_flags_finish.  */
876 };
877 
878 /* Pass data structures.  */
879 class pcrel_opt : public rtl_opt_pass
880 {
881 public:
pcrel_opt(gcc::context * ctxt)882   pcrel_opt (gcc::context *ctxt)
883   : rtl_opt_pass (pass_data_pcrel_opt, ctxt)
884   {}
885 
~pcrel_opt(void)886   ~pcrel_opt (void)
887   {}
888 
889   /* opt_pass methods:  */
gate(function *)890   virtual bool gate (function *)
891   {
892     return (TARGET_PCREL && TARGET_PCREL_OPT && optimize);
893   }
894 
execute(function * fun)895   virtual unsigned int execute (function *fun)
896   {
897     return pcrel_opt_pass (fun);
898   }
899 
clone()900   opt_pass *clone ()
901   {
902     return new pcrel_opt (m_ctxt);
903   }
904 };
905 
906 rtl_opt_pass *
make_pass_pcrel_opt(gcc::context * ctxt)907 make_pass_pcrel_opt (gcc::context *ctxt)
908 {
909   return new pcrel_opt (ctxt);
910 }
911