1 /* Subroutines used support the pc-relative linker optimization.
2 Copyright (C) 2020-2022 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
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:
25 pld addr_reg,var@pcrel@got
27 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
29 lwz data_reg,0(addr_reg)
33 plwz data_reg,var@pcrel
35 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
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
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
51 At the point where it is referenced, we have:
54 pld addr_reg,.Lvar_got@pcrel
56 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
58 lwz data_reg,0(addr_reg)
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
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:
70 pld addr_reg,var@pcrel@got
72 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
74 stw data_reg,0(addr_reg)
78 pstw data_reg,var@pcrel
80 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
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
92 And at our point of reference we have:
95 pld addr_reg,.Lvar_got@pcrel
97 <possibly other insns that do not use 'addr_reg' or 'data_reg'>
99 stw data_reg,0(addr_reg)
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. */
105 #define IN_TARGET_CODE 1
109 #include "coretypes.h"
113 #include "memmodel.h"
120 #include "print-tree.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"
131 /* Various counters. */
133 unsigned long extern_addrs
;
135 unsigned long adjacent_loads
;
136 unsigned long failed_loads
;
137 unsigned long stores
;
138 unsigned long adjacent_stores
;
139 unsigned long failed_stores
;
142 /* Unique integer that is appended to .Lpcrel to make a pcrel_opt label. */
143 static unsigned int pcrel_opt_next_num
;
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.
150 Convert insns of the form:
153 (symbol_ref:DI "ext_symbol"))
157 (set (reg:<MODE> value)
158 (mem:<MODE> (reg:DI addr)))
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))
167 (unspec:DI [(const_int 0)]
168 UNSPEC_PCREL_OPT_LD_DATA))])
172 (parallel [(set (reg:<MODE>)
173 (unspec:<MODE> [(mem:<MODE> (reg:DI addr))
175 (const_int label_num)]
176 UNSPEC_PCREL_OPT_LD_RELOC))
177 (clobber (reg:DI addr))])
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.
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:
187 (unspec:DI [(symbol_ref:DI "ext_symbol")
188 (const_int label_num)]
189 UNSPEC_PCREL_OPT_LD_SAME_REG))
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.
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.
203 pld b,ext_symbol@got@pcrel
208 .reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
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
215 plwz r,ext_symbol@got@pcrel
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. */
225 pcrel_opt_load (rtx_insn
*addr_insn
, rtx_insn
*load_insn
)
227 rtx addr_set
= PATTERN (addr_insn
);
228 gcc_assert (GET_CODE (addr_set
) == SET
);
230 rtx addr_reg
= SET_DEST (addr_set
);
231 gcc_assert (base_reg_operand (addr_reg
, Pmode
));
233 rtx addr_symbol
= SET_SRC (addr_set
);
234 gcc_assert (pcrel_external_address (addr_symbol
, Pmode
));
236 rtx load_set
= PATTERN (load_insn
);
237 gcc_assert (GET_CODE (load_set
) == SET
);
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
))
246 rtx mem
= SET_SRC (load_set
);
247 machine_mode reg_mode
= GET_MODE (reg
);
248 machine_mode mem_mode
= GET_MODE (mem
);
250 unsigned int reg_regno
= reg_or_subregno (reg
);
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
259 if (GET_CODE (mem
) == SIGN_EXTEND
&& GET_MODE (XEXP (mem
, 0)) == SImode
)
261 if (!INT_REGNO_P (reg_regno
))
264 mem_inner
= XEXP (mem
, 0);
268 else if (GET_CODE (mem
) == SIGN_EXTEND
269 || GET_CODE (mem
) == ZERO_EXTEND
270 || GET_CODE (mem
) == FLOAT_EXTEND
)
272 mem_inner
= XEXP (mem
, 0);
273 mem_mode
= GET_MODE (mem_inner
);
276 if (!MEM_P (mem_inner
))
279 /* Can we do PCREL_OPT for this reference? */
280 if (!pcrel_opt_valid_mem_p (reg
, mem_mode
, mem_inner
))
283 /* Allocate a new PC-relative label, and update the load external address
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.
289 (parallel [(set (reg load)
290 (unspec [(symbol_ref addr_symbol)
291 (const_int label_num)]
292 UNSPEC_PCREL_OPT_LD_ADDR))
294 (unspec [(const_int 0)]
295 UNSPEC_PCREL_OPT_LD_DATA))])
297 If the register being loaded is the same as the address register, we use
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
);
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
313 if (addr_regno
!= reg_regno
)
314 addr_pattern
= gen_pcrel_opt_ld_addr (addr_reg
, addr_symbol
, label_num
,
317 addr_pattern
= gen_pcrel_opt_ld_addr_same_reg (addr_reg
, addr_symbol
,
320 validate_change (addr_insn
, &PATTERN (addr_insn
), addr_pattern
, false);
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.
326 (parallel [(set (reg:<MODE> data)
327 (unspec:<MODE> [(mem (addr_reg)
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
);
336 if (GET_CODE (mem
) != GET_CODE (mem_inner
))
337 new_load
= gen_rtx_fmt_e (GET_CODE (mem
), reg_mode
, new_load
);
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
)
345 = gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, new_load_set
, load_clobber
));
347 validate_change (load_insn
, &PATTERN (load_insn
), new_load_pattern
, false);
349 /* Attempt to apply the changes: */
350 if (!apply_change_group ())
352 /* PCREL_OPT load optimization did not succeed. */
353 counters
.failed_loads
++;
356 "PCREL_OPT load failed (addr insn = %d, use insn = %d).\n",
357 INSN_UID (addr_insn
),
358 INSN_UID (load_insn
));
362 /* PCREL_OPT load optimization succeeded. */
364 if (next_nonnote_insn (addr_insn
) == load_insn
)
365 counters
.adjacent_loads
++;
369 "PCREL_OPT load (addr insn = %d, use insn = %d).\n",
370 INSN_UID (addr_insn
),
371 INSN_UID (load_insn
));
373 /* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
374 after we have made changes to the insns. */
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.
383 Convert insns of the form:
386 (symbol_ref:DI "ext_symbol"))
390 (set (mem:<MODE> (reg:DI addr))
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))])
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))])
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.
414 pld b,ext_symbol@got@pcrel
419 .reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
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
426 pstwz r,ext_symbol@got@pcrel
433 pcrel_opt_store (rtx_insn
*addr_insn
, /* insn loading address. */
434 rtx_insn
*store_insn
) /* insn using address. */
436 rtx addr_old_set
= PATTERN (addr_insn
);
437 gcc_assert (GET_CODE (addr_old_set
) == SET
);
439 rtx addr_reg
= SET_DEST (addr_old_set
);
440 gcc_assert (base_reg_operand (addr_reg
, Pmode
));
442 rtx addr_symbol
= SET_SRC (addr_old_set
);
443 gcc_assert (pcrel_external_address (addr_symbol
, Pmode
));
445 rtx store_set
= PATTERN (store_insn
);
446 gcc_assert (GET_CODE (store_set
) == SET
);
448 rtx mem
= SET_DEST (store_set
);
452 machine_mode mem_mode
= GET_MODE (mem
);
453 rtx reg
= SET_SRC (store_set
);
455 /* Don't allow storing the address of the external variable. */
456 if (reg_or_subregno (reg
) == reg_or_subregno (addr_reg
))
459 /* Can we do PCREL_OPT for this reference? */
460 if (!pcrel_opt_valid_mem_p (reg
, mem_mode
, mem
))
463 /* Allocate a new PC-relative label, and update the load address insn.
465 (parallel [(set (reg addr)
466 (unspec [(symbol_ref symbol)
467 (const_int label_num)]
468 UNSPEC_PCREL_OPT_ST_ADDR))
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
);
478 = gen_rtx_PARALLEL (VOIDmode
, gen_rtvec (2, addr_new_set
, addr_use
));
480 validate_change (addr_insn
, &PATTERN (addr_insn
), addr_new_pattern
, false);
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
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
);
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
));
500 validate_change (store_insn
, &PATTERN (store_insn
), new_store_pattern
, false);
502 /* Attempt to apply the changes: */
503 if (!apply_change_group ())
505 /* PCREL_OPT store failed. */
506 counters
.failed_stores
++;
509 "PCREL_OPT store failed (addr insn = %d, use insn = %d).\n",
510 INSN_UID (addr_insn
),
511 INSN_UID (store_insn
));
515 /* PCREL_OPT store succeeded. */
517 if (next_nonnote_insn (addr_insn
) == store_insn
)
518 counters
.adjacent_stores
++;
522 "PCREL_OPT store (addr insn = %d, use insn = %d).\n",
523 INSN_UID (addr_insn
),
524 INSN_UID (store_insn
));
526 /* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
527 after we have made changes to the insns. */
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. */
538 get_mem_base_reg (rtx mem
)
544 if (GET_RTX_CLASS (GET_CODE (mem
)) != RTX_UNARY
545 || GET_CODE (mem
) == BSWAP
)
547 fmt
= GET_RTX_FORMAT (GET_CODE (mem
));
551 if (mem
== NULL_RTX
)
555 if (!MEM_SIZE_KNOWN_P (mem
))
558 rtx addr_rtx
= (XEXP (mem
, 0));
559 if (GET_CODE (addr_rtx
) == PRE_MODIFY
)
560 addr_rtx
= XEXP (addr_rtx
, 1);
562 while (GET_CODE (addr_rtx
) == PLUS
563 && CONST_INT_P (XEXP (addr_rtx
, 1)))
564 addr_rtx
= XEXP (addr_rtx
, 0);
566 if (!REG_P (addr_rtx
))
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. */
578 insn_references_regno_p (rtx_insn
*insn
, unsigned int regno
,
581 struct df_insn_info
*insn_info
= DF_INSN_INFO_GET (insn
);
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
)
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
)
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
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
609 ADDR_INSN is the insn that loads the external symbol address. */
612 pcrel_opt_address (rtx_insn
*addr_insn
)
614 counters
.extern_addrs
++;
616 /* Do some basic validation. */
617 rtx addr_set
= PATTERN (addr_insn
);
618 if (GET_CODE (addr_set
) != SET
)
621 rtx addr_reg
= SET_DEST (addr_set
);
622 rtx addr_symbol
= SET_SRC (addr_set
);
624 if (!base_reg_operand (addr_reg
, Pmode
)
625 || !pcrel_external_address (addr_symbol
, Pmode
))
628 /* The address register must have exactly one definition. */
629 struct df_insn_info
*insn_info
= DF_INSN_INFO_GET (addr_insn
);
633 df_ref def
= df_single_def (insn_info
);
637 /* Make sure there is at least one use. */
638 df_link
*chain
= DF_REF_CHAIN (def
);
639 if (!chain
|| !chain
->ref
)
642 /* Get the insn of the possible load or store. */
643 rtx_insn
*use_insn
= DF_REF_INSN (chain
->ref
);
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
))
649 gcc_assert (DF_REF_INSN (chain
->ref
));
650 if (NONDEBUG_INSN_P (DF_REF_INSN (chain
->ref
)))
654 /* The use instruction must be a single non-prefixed instruction. */
655 if (get_attr_length (use_insn
) != 4)
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
))
662 /* If this isn't a simple SET, skip doing the optimization. */
663 if (GET_CODE (PATTERN (use_insn
)) != SET
)
666 enum attr_type use_insn_type
= get_attr_type (use_insn
);
667 unsigned int use_regno
;
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
)
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
))))
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
)))
686 use_regno
= REGNO (use_dest
);
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
))))
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
)))
699 use_regno
= REGNO (use_src
);
702 /* We can only optimize loads and stores. Ignore everything else. */
707 for (insn
= NEXT_INSN (addr_insn
);
709 insn
= NEXT_INSN (insn
))
711 /* If we see a call, do not do the PCREL_OPT optimization. */
715 /* Skip debug insns. */
716 if (!NONDEBUG_INSN_P (insn
))
719 /* See if it is a load or store. */
720 if (GET_CODE (PATTERN (insn
)) != USE
721 && GET_CODE (PATTERN (insn
)) != CLOBBER
)
723 switch (get_attr_type (insn
))
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
731 if (get_attr_loads_external_address (insn
)
732 == LOADS_EXTERNAL_ADDRESS_YES
)
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
742 if (use_insn_type
== TYPE_STORE
743 || use_insn_type
== TYPE_FPSTORE
744 || use_insn_type
== 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. */
760 /* Don't do the optimization through atomic operations. */
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
))
774 /* Is this a load or a store? */
775 switch (use_insn_type
)
780 pcrel_opt_load (addr_insn
, use_insn
);
786 pcrel_opt_store (addr_insn
, use_insn
);
794 /* Optimize pcrel external variable references. */
797 pcrel_opt_pass (function
*fun
)
800 rtx_insn
*insn
, *curr_insn
= 0;
802 memset (&counters
, 0, sizeof (counters
));
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 ();
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
);
817 fprintf (dump_file
, "\n");
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
822 FOR_ALL_BB_FN (bb
, fun
)
824 FOR_BB_INSNS_SAFE (bb
, insn
, curr_insn
)
826 if (NONJUMP_INSN_P (insn
)
828 && get_attr_loads_external_address (insn
)
829 == LOADS_EXTERNAL_ADDRESS_YES
)
830 pcrel_opt_address (insn
);
837 "\n# of loads of an address of an external symbol = %lu\n",
838 counters
.extern_addrs
);
840 fprintf (dump_file
, "# of PCREL_OPT loads = %lu (adjacent %lu)\n",
841 counters
.loads
, counters
.adjacent_loads
);
843 if (counters
.failed_loads
)
844 fprintf (dump_file
, "# of failed PCREL_OPT loads = %lu\n",
845 counters
.failed_loads
);
847 fprintf (dump_file
, "# of PCREL_OPT stores = %lu (adjacent %lu)\n",
848 counters
.stores
, counters
.adjacent_stores
);
850 if (counters
.failed_stores
)
851 fprintf (dump_file
, "# of failed PCREL_OPT stores = %lu\n",
852 counters
.failed_stores
);
854 fprintf (dump_file
, "\n");
857 df_remove_problem (df_chain
);
858 df_process_deferred_rescans ();
859 df_set_flags (DF_RD_PRUNE_DEAD_DEFS
| DF_LR_RUN_DCE
);
864 /* Optimize pc-relative references for the new PCREL_OPT pass. */
865 const pass_data pass_data_pcrel_opt
=
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. */
878 /* Pass data structures. */
879 class pcrel_opt
: public rtl_opt_pass
882 pcrel_opt (gcc::context
*ctxt
)
883 : rtl_opt_pass (pass_data_pcrel_opt
, ctxt
)
889 /* opt_pass methods: */
890 virtual bool gate (function
*)
892 return (TARGET_PCREL
&& TARGET_PCREL_OPT
&& optimize
);
895 virtual unsigned int execute (function
*fun
)
897 return pcrel_opt_pass (fun
);
902 return new pcrel_opt (m_ctxt
);
907 make_pass_pcrel_opt (gcc::context
*ctxt
)
909 return new pcrel_opt (ctxt
);