mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-21 14:41:07 +08:00
Optimize pcrel access of globals
This patch implements a RTL pass that looks for pc-relative loads of the address of an external variable using the PCREL_GOT relocation and a single load or store that uses that external address. Produced by a cast of thousands: * Michael Meissner * Peter Bergner * Bill Schmidt * Alan Modra * Segher Boessenkool * Aaron Sawdey gcc/ChangeLog: * config.gcc: Add rs6000-pcrel-opt.o. * config/rs6000/rs6000-pcrel-opt.c: New file. * config/rs6000/pcrel-opt.md: New file. * config/rs6000/predicates.md: Add d_form_memory predicate. * config/rs6000/rs6000-cpus.def: Add OPTION_MASK_PCREL_OPT. * config/rs6000/rs6000-passes.def: Add pass_pcrel_opt. * config/rs6000/rs6000-protos.h: Add reg_to_non_prefixed(), pcrel_opt_valid_mem_p(), output_pcrel_opt_reloc(), and make_pass_pcrel_opt(). * config/rs6000/rs6000.c (reg_to_non_prefixed): Make global. (rs6000_option_override_internal): Add pcrel-opt. (rs6000_delegitimize_address): Support pcrel-opt. (rs6000_opt_masks): Add pcrel-opt. (pcrel_opt_valid_mem_p): New function. (reg_to_non_prefixed): Make global. (rs6000_asm_output_opcode): Reset prepend_p_to_next_insn. (output_pcrel_opt_reloc): New function. * config/rs6000/rs6000.md (loads_extern_addr): New attr. (pcrel_extern_addr): Set loads_extern_addr. Add include for pcrel-opt.md. * config/rs6000/rs6000.opt: Add -mpcrel-opt. * config/rs6000/t-rs6000: Add rules for pcrel-opt.c and pcrel-opt.md. gcc/testsuite/ChangeLog: * gcc.target/powerpc/pcrel-opt-inc-di.c: New test. * gcc.target/powerpc/pcrel-opt-ld-df.c: New test. * gcc.target/powerpc/pcrel-opt-ld-di.c: New test. * gcc.target/powerpc/pcrel-opt-ld-hi.c: New test. * gcc.target/powerpc/pcrel-opt-ld-qi.c: New test. * gcc.target/powerpc/pcrel-opt-ld-sf.c: New test. * gcc.target/powerpc/pcrel-opt-ld-si.c: New test. * gcc.target/powerpc/pcrel-opt-ld-vector.c: New test. * gcc.target/powerpc/pcrel-opt-st-df.c: New test. * gcc.target/powerpc/pcrel-opt-st-di.c: New test. * gcc.target/powerpc/pcrel-opt-st-hi.c: New test. * gcc.target/powerpc/pcrel-opt-st-qi.c: New test. * gcc.target/powerpc/pcrel-opt-st-sf.c: New test. * gcc.target/powerpc/pcrel-opt-st-si.c: New test. * gcc.target/powerpc/pcrel-opt-st-vector.c: New test.
This commit is contained in:
parent
9967bbfced
commit
b8d85f569a
@ -509,7 +509,8 @@ or1k*-*-*)
|
||||
;;
|
||||
powerpc*-*-*)
|
||||
cpu_type=rs6000
|
||||
extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o rs6000-call.o"
|
||||
extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o"
|
||||
extra_objs="${extra_objs} rs6000-call.o rs6000-pcrel-opt.o"
|
||||
extra_headers="ppc-asm.h altivec.h htmintrin.h htmxlintrin.h"
|
||||
extra_headers="${extra_headers} bmi2intrin.h bmiintrin.h"
|
||||
extra_headers="${extra_headers} xmmintrin.h mm_malloc.h emmintrin.h"
|
||||
@ -524,6 +525,7 @@ powerpc*-*-*)
|
||||
esac
|
||||
extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt"
|
||||
target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c"
|
||||
target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel-opt.c"
|
||||
;;
|
||||
pru-*-*)
|
||||
cpu_type=pru
|
||||
@ -535,8 +537,10 @@ riscv*)
|
||||
;;
|
||||
rs6000*-*-*)
|
||||
extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt"
|
||||
extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o rs6000-call.o"
|
||||
extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o"
|
||||
extra_objs="${extra_objs} rs6000-call.o rs6000-pcrel-opt.o"
|
||||
target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c"
|
||||
target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel-opt.c"
|
||||
;;
|
||||
sparc*-*-*)
|
||||
cpu_type=sparc
|
||||
|
401
gcc/config/rs6000/pcrel-opt.md
Normal file
401
gcc/config/rs6000/pcrel-opt.md
Normal file
@ -0,0 +1,401 @@
|
||||
;; Machine description for the PCREL_OPT optimization.
|
||||
;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
;; Contributed by Michael Meissner (meissner@linux.ibm.com)
|
||||
|
||||
;; This file is part of GCC.
|
||||
|
||||
;; GCC is free software; you can redistribute it and/or modify it
|
||||
;; under the terms of the GNU General Public License as published
|
||||
;; by the Free Software Foundation; either version 3, or (at your
|
||||
;; option) any later version.
|
||||
|
||||
;; GCC is distributed in the hope that it will be useful, but WITHOUT
|
||||
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
;; License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GCC; see the file COPYING3. If not see
|
||||
;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
;; Support for the PCREL_OPT optimization. PCREL_OPT looks for instances where
|
||||
;; an external variable is used only once, either for reading or for writing.
|
||||
;;
|
||||
;; If we are optimizing a single read, normally the code would look like:
|
||||
;;
|
||||
;; (set (reg:DI <ptr>)
|
||||
;; (symbol_ref:DI "<extern_addr>")) # <data> is currently dead
|
||||
;;
|
||||
;; ... # insns do not need to be adjacent
|
||||
;;
|
||||
;; (set (reg:SI <data>)
|
||||
;; (mem:SI (reg:DI <xxx>))) # <ptr> dies with this insn
|
||||
;;
|
||||
;; We transform this into:
|
||||
;;
|
||||
;; (parallel [(set (reg:DI <ptr>)
|
||||
;; (unspec:SI [(symbol_ref:DI <extern_addr>)
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_LD_ADDR))
|
||||
;; (set (reg:DI <data>)
|
||||
;; (unspec:DI [(const_int 0)]
|
||||
;; UNSPEC_PCREL_OPT_LD_DATA))])
|
||||
;;
|
||||
;; ...
|
||||
;;
|
||||
;; (parallel [(set (reg:SI <data>)
|
||||
;; (unspec:SI [(mem:SI (reg:DI <ptr>))
|
||||
;; (reg:DI <data>)
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
;; (clobber (reg:DI <ptr>))])
|
||||
;;
|
||||
;; The marker is an integer constant that links the load of the external
|
||||
;; address to the load of the actual variable.
|
||||
;;
|
||||
;; In the first insn, we set both the address of the external variable, and
|
||||
;; mark that the variable being loaded both are created in that insn, and are
|
||||
;; consumed in the second insn. It doesn't matter what mode the register that
|
||||
;; we will ultimately do the load into, so we use DImode. We just need to mark
|
||||
;; that both registers may be set in the first insn, and will be used in the
|
||||
;; second insn.
|
||||
;;
|
||||
;; Since we use UNSPEC's and link both the the register holding the external
|
||||
;; address and the value being loaded, it should prevent other passes from
|
||||
;; modifying it.
|
||||
;;
|
||||
;; If the register being loaded is the same as the base register, we use an
|
||||
;; alternate form of the insns.
|
||||
;;
|
||||
;; (set (reg:DI <data_ptr>)
|
||||
;; (unspec:DI [(symbol_ref:DI <extern_addr>)
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_LD_SAME_REG))
|
||||
;;
|
||||
;; ...
|
||||
;;
|
||||
;; (parallel [(set (reg:SI <data>)
|
||||
;; (unspec:SI [(mem:SI (reg:DI <ptr>))
|
||||
;; (reg:DI <data>)
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
;; (clobber (reg:DI <ptr>))])
|
||||
|
||||
(define_c_enum "unspec"
|
||||
[UNSPEC_PCREL_OPT_LD_ADDR
|
||||
UNSPEC_PCREL_OPT_LD_DATA
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG
|
||||
UNSPEC_PCREL_OPT_LD_RELOC
|
||||
UNSPEC_PCREL_OPT_ST_ADDR
|
||||
UNSPEC_PCREL_OPT_ST_RELOC])
|
||||
|
||||
;; Modes that are supported for PCREL_OPT
|
||||
(define_mode_iterator PCRELOPT [QI HI SI DI TI SF DF KF
|
||||
V1TI V2DI V4SI V8HI V16QI V2DF V4SF
|
||||
(TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")])
|
||||
|
||||
;; Vector modes for PCREL_OPT
|
||||
(define_mode_iterator PCRELOPT_VECT [TI KF V1TI V2DI V4SI V8HI V16QI V2DF V4SF
|
||||
(TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")])
|
||||
|
||||
;; Insn for loading the external address, where the register being loaded is not
|
||||
;; the same as the register being loaded with the data.
|
||||
(define_insn "pcrel_opt_ld_addr"
|
||||
[(set (match_operand:DI 0 "base_reg_operand" "=&b,&b")
|
||||
(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
|
||||
(match_operand 2 "const_int_operand" "n,n")]
|
||||
UNSPEC_PCREL_OPT_LD_ADDR))
|
||||
(set (match_operand:DI 3 "gpc_reg_operand" "=r,wa")
|
||||
(unspec:DI [(const_int 0)]
|
||||
UNSPEC_PCREL_OPT_LD_DATA))]
|
||||
"TARGET_PCREL_OPT
|
||||
&& reg_or_subregno (operands[0]) != reg_or_subregno (operands[3])"
|
||||
"ld %0,%a1\n.Lpcrel%2:"
|
||||
[(set_attr "prefixed" "yes")
|
||||
(set_attr "type" "load")
|
||||
(set_attr "loads_external_address" "yes")])
|
||||
|
||||
;; Alternate form of loading up the external address that is the same register
|
||||
;; as the final load.
|
||||
(define_insn "pcrel_opt_ld_addr_same_reg"
|
||||
[(set (match_operand:DI 0 "base_reg_operand" "=b")
|
||||
(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
|
||||
(match_operand 2 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG))]
|
||||
"TARGET_PCREL_OPT"
|
||||
"ld %0,%a1\n.Lpcrel%2:"
|
||||
[(set_attr "prefixed" "yes")
|
||||
(set_attr "type" "load")
|
||||
(set_attr "loads_external_address" "yes")])
|
||||
|
||||
;; PCREL_OPT modes that are optimized for loading or storing GPRs.
|
||||
(define_mode_iterator PCRELOPT_GPR [QI HI SI DI SF DF])
|
||||
|
||||
(define_mode_attr PCRELOPT_GPR_LD [(QI "lbz")
|
||||
(HI "lhz")
|
||||
(SI "lwz")
|
||||
(SF "lwz")
|
||||
(DI "ld")
|
||||
(DF "ld")])
|
||||
|
||||
;; PCREL_OPT load operation of GPRs. Operand 4 (the register used to hold the
|
||||
;; address of the external symbol) is SCRATCH if the same register is used for
|
||||
;; the normal load.
|
||||
(define_insn "*pcrel_opt_ld<mode>_gpr"
|
||||
[(parallel [(set (match_operand:PCRELOPT_GPR 0 "int_reg_operand" "+r")
|
||||
(unspec:PCRELOPT_GPR [
|
||||
(match_operand:PCRELOPT_GPR 1 "d_form_memory" "m")
|
||||
(match_operand:DI 2 "int_reg_operand" "0")
|
||||
(match_operand 3 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
(clobber (match_scratch:DI 4 "=bX"))])]
|
||||
"TARGET_PCREL_OPT
|
||||
&& (GET_CODE (operands[4]) == SCRATCH
|
||||
|| reg_mentioned_p (operands[4], operands[1]))"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return "<PCRELOPT_GPR_LD> %0,%1";
|
||||
}
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
;; PCREL_OPT load with sign/zero extension
|
||||
(define_insn "*pcrel_opt_ldsi_<u><mode>_gpr"
|
||||
[(set (match_operand:EXTSI 0 "int_reg_operand" "+r")
|
||||
(any_extend:EXTSI
|
||||
(unspec:SI [(match_operand:SI 1 "d_form_memory" "m")
|
||||
(match_operand:DI 2 "int_reg_operand" "0")
|
||||
(match_operand 3 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC)))
|
||||
(clobber (match_scratch:DI 4 "=bX"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return "lw<az> %0,%1";
|
||||
}
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*pcrel_opt_ldhi_<u><mode>_gpr"
|
||||
[(set (match_operand:EXTHI 0 "int_reg_operand" "+r")
|
||||
(any_extend:EXTHI
|
||||
(unspec:HI [(match_operand:HI 1 "d_form_memory" "m")
|
||||
(match_operand:DI 2 "int_reg_operand" "0")
|
||||
(match_operand 3 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC)))
|
||||
(clobber (match_scratch:DI 4 "=bX"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return "lh<az> %0,%1";
|
||||
}
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*pcrel_opt_ldqi_u<mode>_gpr"
|
||||
[(set (match_operand:EXTQI 0 "int_reg_operand" "+r")
|
||||
(zero_extend:EXTQI
|
||||
(unspec:QI [(match_operand:QI 1 "d_form_memory" "m")
|
||||
(match_operand:DI 2 "int_reg_operand" "0")
|
||||
(match_operand 3 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC)))
|
||||
(clobber (match_scratch:DI 4 "=bX"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return "lbz %0,%1";
|
||||
}
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
;; Scalar types that can be optimized by loading them into floating point
|
||||
;; or Altivec registers.
|
||||
(define_mode_iterator PCRELOPT_FP [DI DF SF])
|
||||
|
||||
;; Load instructions to load up scalar floating point or 64-bit integer values
|
||||
;; into floating point registers or Altivec registers.
|
||||
(define_mode_attr PCRELOPT_FPR_LD [(DI "lfd") (DF "lfd") (SF "lfs")])
|
||||
(define_mode_attr PCRELOPT_VMX_LD [(DI "lxsd") (DF "lxsd") (SF "lxssp")])
|
||||
|
||||
;; PCREL_OPT load operation of scalar DF/DI/SF into vector registers.
|
||||
(define_insn "*pcrel_opt_ld<mode>_vsx"
|
||||
[(set (match_operand:PCRELOPT_FP 0 "vsx_register_operand" "+d,v")
|
||||
(unspec:PCRELOPT_FP [(match_operand:PCRELOPT_FP 1 "d_form_memory" "m,m")
|
||||
(match_operand:DI 2 "vsx_register_operand" "0,0")
|
||||
(match_operand 3 "const_int_operand" "n,n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
(clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return which_alternative ? "<PCRELOPT_VMX_LD> %0,%1"
|
||||
: "<PCRELOPT_FPR_LD> %0,%1";
|
||||
}
|
||||
[(set_attr "type" "fpload")])
|
||||
|
||||
;; PCREL_OPT optimization extending SFmode to DFmode via a load.
|
||||
(define_insn "*pcrel_opt_ldsf_df"
|
||||
[(set (match_operand:DF 0 "vsx_register_operand" "+d,v")
|
||||
(float_extend:DF
|
||||
(unspec:SF [(match_operand:SF 1 "d_form_memory" "m,m")
|
||||
(match_operand:DI 2 "vsx_register_operand" "0,0")
|
||||
(match_operand 3 "const_int_operand" "n,n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC)))
|
||||
(clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return which_alternative ? "lxssp %0,%1" : "lfs %0,%1";
|
||||
}
|
||||
[(set_attr "type" "fpload")])
|
||||
|
||||
;; PCREL_OPT load operation of vector/float128 types into vector registers.
|
||||
(define_insn "*pcrel_opt_ld<mode>"
|
||||
[(set (match_operand:PCRELOPT_VECT 0 "vsx_register_operand" "+wa")
|
||||
(unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "d_form_memory" "m")
|
||||
(match_operand:DI 2 "vsx_register_operand" "0")
|
||||
(match_operand 3 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
(clobber (match_operand:DI 4 "base_reg_operand" "=b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[3]);
|
||||
return "lxv %x0,%1";
|
||||
}
|
||||
[(set_attr "type" "vecload")])
|
||||
|
||||
|
||||
;; PCREL_OPT optimization for stores. We need to put the label after the PLD
|
||||
;; instruction, because the assembler might insert a NOP before the PLD for
|
||||
;; alignment.
|
||||
;;
|
||||
;; If we are optimizing a single write, normally the code would look like:
|
||||
;;
|
||||
;; (set (reg:DI <ptr>)
|
||||
;; (symbol_ref:DI "<extern_addr>")) # <data> must be live here
|
||||
;;
|
||||
;; ... # insns do not need to be adjacent
|
||||
;;
|
||||
;; (set (mem:SI (reg:DI <xxx>))
|
||||
;; (reg:SI <data>)) # <ptr> dies with this insn
|
||||
;;
|
||||
;; We optimize this to be:
|
||||
;;
|
||||
;; (parallel [(set (reg:DI <ptr>)
|
||||
;; (unspec:DI [(symbol_ref:DI "<extern_addr>")
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_ST_ADDR))
|
||||
;; (use (reg:<MODE> <data>))])
|
||||
;;
|
||||
;; ... # insns do not need to be adjacent
|
||||
;;
|
||||
;; (parallel [(set (mem:<MODE> (reg:DI <ptr>))
|
||||
;; (unspec:<MODE> [(reg:<MODE> <data>)
|
||||
;; (const_int <marker>)]
|
||||
;; UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
;; (clobber (reg:DI <ptr>))])
|
||||
|
||||
(define_insn "*pcrel_opt_st_addr<mode>"
|
||||
[(set (match_operand:DI 0 "gpc_reg_operand" "=b")
|
||||
(unspec:DI [(match_operand:DI 1 "pcrel_external_address")
|
||||
(match_operand 2 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_ST_ADDR))
|
||||
(use (match_operand:PCRELOPT 3 "gpc_reg_operand" "rwa"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
"ld %0,%a1\n.Lpcrel%2:"
|
||||
[(set_attr "prefixed" "yes")
|
||||
(set_attr "type" "load")
|
||||
(set_attr "loads_external_address" "yes")])
|
||||
|
||||
;; PCREL_OPT stores.
|
||||
(define_insn "*pcrel_opt_st<mode>"
|
||||
[(set (match_operand:QHSI 0 "d_form_memory" "=m")
|
||||
(unspec:QHSI [(match_operand:QHSI 1 "gpc_reg_operand" "r")
|
||||
(match_operand 2 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (match_operand:DI 3 "base_reg_operand" "=b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[2]);
|
||||
return "st<wd> %1,%0";
|
||||
}
|
||||
[(set_attr "type" "store")])
|
||||
|
||||
(define_insn "*pcrel_opt_stdi"
|
||||
[(set (match_operand:DI 0 "d_form_memory" "=m,m,m")
|
||||
(unspec:DI [(match_operand:DI 1 "gpc_reg_operand" "r,d,v")
|
||||
(match_operand 2 "const_int_operand" "n,n,n")]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
|
||||
"TARGET_PCREL_OPT && TARGET_POWERPC64"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[2]);
|
||||
switch (which_alternative)
|
||||
{
|
||||
case 0:
|
||||
return "std %1,%0";
|
||||
case 1:
|
||||
return "stfd %1,%0";
|
||||
case 2:
|
||||
return "stxsd %1,%0";
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
[(set_attr "type" "store,fpstore,fpstore")])
|
||||
|
||||
(define_insn "*pcrel_opt_stsf"
|
||||
[(set (match_operand:SF 0 "d_form_memory" "=m,m,m")
|
||||
(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "d,v,r")
|
||||
(match_operand 2 "const_int_operand" "n,n,n")]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[2]);
|
||||
switch (which_alternative)
|
||||
{
|
||||
case 0:
|
||||
return "stfs %1,%0";
|
||||
case 1:
|
||||
return "stxssp %1,%0";
|
||||
case 2:
|
||||
return "stw %1,%0";
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
[(set_attr "type" "fpstore,fpstore,store")])
|
||||
|
||||
(define_insn "*pcrel_opt_stdf"
|
||||
[(set (match_operand:DF 0 "d_form_memory" "=m,m,m")
|
||||
(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d,v,r")
|
||||
(match_operand 2 "const_int_operand" "n,n,n")]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))]
|
||||
"TARGET_PCREL_OPT
|
||||
&& (TARGET_POWERPC64 || vsx_register_operand (operands[1], DFmode))"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[2]);
|
||||
switch (which_alternative)
|
||||
{
|
||||
case 0:
|
||||
return "stfd %1,%0";
|
||||
case 1:
|
||||
return "stxsd %1,%0";
|
||||
case 2:
|
||||
return "std %1,%0";
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
[(set_attr "type" "fpstore,fpstore,store")])
|
||||
|
||||
(define_insn "*pcrel_opt_st<mode>"
|
||||
[(set (match_operand:PCRELOPT_VECT 0 "d_form_memory" "=m")
|
||||
(unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "gpc_reg_operand" "wa")
|
||||
(match_operand 2 "const_int_operand" "n")]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (match_operand:DI 3 "base_reg_operand" "=b"))]
|
||||
"TARGET_PCREL_OPT"
|
||||
{
|
||||
output_pcrel_opt_reloc (operands[2]);
|
||||
return "stxv %x1,%0";
|
||||
}
|
||||
[(set_attr "type" "vecstore")])
|
@ -1904,3 +1904,24 @@
|
||||
{
|
||||
return address_is_prefixed (XEXP (op, 0), mode, NON_PREFIXED_DEFAULT);
|
||||
})
|
||||
|
||||
;; Return true if the operand is a valid memory operand with a D-form
|
||||
;; address that could be merged with the load of a PC-relative external address
|
||||
;; with the PCREL_OPT optimization. We don't check here whether or not the
|
||||
;; offset needs to be used in a DS-FORM (bottom 2 bits 0) or DQ-FORM (bottom 4
|
||||
;; bits 0) instruction.
|
||||
(define_predicate "d_form_memory"
|
||||
(match_code "mem")
|
||||
{
|
||||
if (!memory_operand (op, mode))
|
||||
return false;
|
||||
|
||||
rtx addr = XEXP (op, 0);
|
||||
|
||||
if (REG_P (addr))
|
||||
return true;
|
||||
if (SUBREG_P (addr) && REG_P (SUBREG_REG (addr)))
|
||||
return true;
|
||||
|
||||
return !indexed_address (addr, mode);
|
||||
})
|
||||
|
@ -77,6 +77,7 @@
|
||||
/* Flags that need to be turned off if -mno-power10. */
|
||||
#define OTHER_POWER10_MASKS (OPTION_MASK_MMA \
|
||||
| OPTION_MASK_PCREL \
|
||||
| OPTION_MASK_PCREL_OPT \
|
||||
| OPTION_MASK_PREFIXED)
|
||||
|
||||
#define ISA_3_1_MASKS_SERVER (ISA_3_0_MASKS_SERVER \
|
||||
@ -147,6 +148,7 @@
|
||||
| OPTION_MASK_P9_MISC \
|
||||
| OPTION_MASK_P9_VECTOR \
|
||||
| OPTION_MASK_PCREL \
|
||||
| OPTION_MASK_PCREL_OPT \
|
||||
| OPTION_MASK_POPCNTB \
|
||||
| OPTION_MASK_POPCNTD \
|
||||
| OPTION_MASK_POWERPC64 \
|
||||
|
@ -24,4 +24,12 @@ along with GCC; see the file COPYING3. If not see
|
||||
REPLACE_PASS (PASS, INSTANCE, TGT_PASS)
|
||||
*/
|
||||
|
||||
/* Pass to add the appropriate vector swaps on power8 little endian systems.
|
||||
The power8 does not have instructions that automaticaly do the byte swaps
|
||||
for loads and stores. */
|
||||
INSERT_PASS_BEFORE (pass_cse, 1, pass_analyze_swaps);
|
||||
|
||||
/* Pass to do the PCREL_OPT optimization that combines the load of an
|
||||
external symbol's address along with a single load or store using that
|
||||
address as a base register. */
|
||||
INSERT_PASS_BEFORE (pass_sched2, 1, pass_pcrel_opt);
|
||||
|
910
gcc/config/rs6000/rs6000-pcrel-opt.c
Normal file
910
gcc/config/rs6000/rs6000-pcrel-opt.c
Normal file
@ -0,0 +1,910 @@
|
||||
/* Subroutines used support the pc-relative linker optimization.
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
by the Free Software Foundation; either version 3, or (at your
|
||||
option) any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This file implements a RTL pass that looks for pc-relative loads of the
|
||||
address of an external variable using the PCREL_GOT relocation and a single
|
||||
load that uses that external address. If that is found we create the
|
||||
PCREL_OPT relocation to possibly convert:
|
||||
|
||||
pld addr_reg,var@pcrel@got
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
lwz data_reg,0(addr_reg)
|
||||
|
||||
into:
|
||||
|
||||
plwz data_reg,var@pcrel
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
nop
|
||||
|
||||
Of course it would be nice to be able to put the plwz in this example in
|
||||
place of the lwz but the linker cannot easily replace a 4-byte instruction
|
||||
with an 8-byte one.
|
||||
|
||||
If the variable is not defined in the main program or the code using it is
|
||||
not in the main program, the linker puts the address in the .got section and
|
||||
generates:
|
||||
|
||||
.section .got
|
||||
.Lvar_got:
|
||||
.dword var
|
||||
|
||||
At the point where it is referenced, we have:
|
||||
|
||||
.section .text
|
||||
pld addr_reg,.Lvar_got@pcrel
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
lwz data_reg,0(addr_reg)
|
||||
|
||||
We look for a single usage in the basic block where this external
|
||||
address is loaded, and convert it to a PCREL_OPT relocation so the
|
||||
linker can convert it to a single plwz in this case. Multiple uses
|
||||
or references in another basic block will force us to not use the
|
||||
PCREL_OPT relocation.
|
||||
|
||||
We also optimize stores to the address of an external variable using the
|
||||
PCREL_GOT relocation and a single store that uses that external address. If
|
||||
that is found we create the PCREL_OPT relocation to possibly convert:
|
||||
|
||||
pld addr_reg,var@pcrel@got
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
stw data_reg,0(addr_reg)
|
||||
|
||||
into:
|
||||
|
||||
pstw data_reg,var@pcrel
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
nop
|
||||
|
||||
If the variable is not defined in the main program or the code using it is
|
||||
not in the main program, the linker puts the address in the .got section and
|
||||
generates:
|
||||
|
||||
.section .got
|
||||
.Lvar_got:
|
||||
.dword var
|
||||
|
||||
And at our point of reference we have:
|
||||
|
||||
.section .text
|
||||
pld addr_reg,.Lvar_got@pcrel
|
||||
|
||||
<possibly other insns that do not use 'addr_reg' or 'data_reg'>
|
||||
|
||||
stw data_reg,0(addr_reg)
|
||||
|
||||
We only look for a single usage in the basic block where the external
|
||||
address is loaded. Multiple uses or references in another basic block will
|
||||
force us to not use the PCREL_OPT relocation. */
|
||||
|
||||
#define IN_TARGET_CODE 1
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "backend.h"
|
||||
#include "rtl.h"
|
||||
#include "tree.h"
|
||||
#include "memmodel.h"
|
||||
#include "expmed.h"
|
||||
#include "optabs.h"
|
||||
#include "recog.h"
|
||||
#include "df.h"
|
||||
#include "tm_p.h"
|
||||
#include "ira.h"
|
||||
#include "print-tree.h"
|
||||
#include "varasm.h"
|
||||
#include "explow.h"
|
||||
#include "expr.h"
|
||||
#include "output.h"
|
||||
#include "tree-pass.h"
|
||||
#include "rtx-vector-builder.h"
|
||||
#include "print-rtl.h"
|
||||
#include "insn-attr.h"
|
||||
#include "insn-codes.h"
|
||||
|
||||
/* Various counters. */
|
||||
static struct {
|
||||
unsigned long extern_addrs;
|
||||
unsigned long loads;
|
||||
unsigned long adjacent_loads;
|
||||
unsigned long failed_loads;
|
||||
unsigned long stores;
|
||||
unsigned long adjacent_stores;
|
||||
unsigned long failed_stores;
|
||||
} counters;
|
||||
|
||||
/* Unique integer that is appended to .Lpcrel to make a pcrel_opt label. */
|
||||
static unsigned int pcrel_opt_next_num;
|
||||
|
||||
|
||||
/* Optimize a PC-relative load address to be used in a load. Before it calls
|
||||
this function, pcrel_opt_address () uses DF to make sure that it is safe
|
||||
to do the PCREL_OPT optimization on these insns.
|
||||
|
||||
Convert insns of the form:
|
||||
|
||||
(set (reg:DI addr)
|
||||
(symbol_ref:DI "ext_symbol"))
|
||||
|
||||
...
|
||||
|
||||
(set (reg:<MODE> value)
|
||||
(mem:<MODE> (reg:DI addr)))
|
||||
|
||||
into:
|
||||
|
||||
(parallel [(set (reg:DI addr)
|
||||
(unspec:<MODE> [(symbol_ref:DI "ext_symbol")
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_ADDR))
|
||||
(set (reg:DI data)
|
||||
(unspec:DI [(const_int 0)]
|
||||
UNSPEC_PCREL_OPT_LD_DATA))])
|
||||
|
||||
...
|
||||
|
||||
(parallel [(set (reg:<MODE>)
|
||||
(unspec:<MODE> [(mem:<MODE> (reg:DI addr))
|
||||
(reg:DI data)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
(clobber (reg:DI addr))])
|
||||
|
||||
Because PCREL_OPT will move the actual location of the load from the second
|
||||
insn to the first, we need to have the register for the load data be live
|
||||
starting at the first insn.
|
||||
|
||||
If the destination register for the data being loaded is the same register
|
||||
used to hold the extern address, we generate this insn instead:
|
||||
|
||||
(set (reg:DI data)
|
||||
(unspec:DI [(symbol_ref:DI "ext_symbol")
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG))
|
||||
|
||||
In the first insn, we set both the address of the external variable, and mark
|
||||
that the variable being loaded both are created in that insn, and are
|
||||
consumed in the second insn. The mode used in the first insn for the data
|
||||
register that will be loaded in the second insn doesn't matter in the end so
|
||||
we use DImode. We just need to mark that both registers may be set in the
|
||||
first insn, and will be used in the second insn.
|
||||
|
||||
The UNSPEC_PCREL_OPT_LD_ADDR insn will generate the load address plus
|
||||
a definition of a label (.Lpcrel<n>), while the UNSPEC_PCREL_OPT_LD_RELOC
|
||||
insn will generate the .reloc to tell the linker to tie the load address and
|
||||
load using that address together.
|
||||
|
||||
pld b,ext_symbol@got@pcrel
|
||||
.Lpcrel1:
|
||||
|
||||
...
|
||||
|
||||
.reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
|
||||
lwz r,0(b)
|
||||
|
||||
If ext_symbol is defined in another object file in the main program and we
|
||||
are linking the main program, the linker will convert the above instructions
|
||||
to:
|
||||
|
||||
plwz r,ext_symbol@got@pcrel
|
||||
|
||||
...
|
||||
|
||||
nop
|
||||
|
||||
ADDR_INSN is the insn that is loading the address.
|
||||
LOAD_INSN is the insn that uses the address to load the actual data. */
|
||||
|
||||
static void
|
||||
pcrel_opt_load (rtx_insn *addr_insn, rtx_insn *load_insn)
|
||||
{
|
||||
rtx addr_set = PATTERN (addr_insn);
|
||||
gcc_assert (GET_CODE (addr_set) == SET);
|
||||
|
||||
rtx addr_reg = SET_DEST (addr_set);
|
||||
gcc_assert (base_reg_operand (addr_reg, Pmode));
|
||||
|
||||
rtx addr_symbol = SET_SRC (addr_set);
|
||||
gcc_assert (pcrel_external_address (addr_symbol, Pmode));
|
||||
|
||||
rtx load_set = PATTERN (load_insn);
|
||||
gcc_assert (GET_CODE (load_set) == SET);
|
||||
|
||||
/* Make sure there are no references to the register being loaded
|
||||
between the two insns. */
|
||||
rtx reg = SET_DEST (load_set);
|
||||
if (reg_used_between_p (reg, addr_insn, load_insn)
|
||||
|| reg_set_between_p (reg, addr_insn, load_insn))
|
||||
return;
|
||||
|
||||
rtx mem = SET_SRC (load_set);
|
||||
machine_mode reg_mode = GET_MODE (reg);
|
||||
machine_mode mem_mode = GET_MODE (mem);
|
||||
rtx mem_inner = mem;
|
||||
unsigned int reg_regno = reg_or_subregno (reg);
|
||||
|
||||
/* Handle the fact that LWA is a DS format instruction, but LWZ is a D format
|
||||
instruction. If the mem load is a signed SImode (i.e. LWA would be used)
|
||||
we set mem_mode to DImode so that pcrel_opt_valid_mem_p() will check that
|
||||
the address will work for a DS-form instruction. If it won't work, we skip
|
||||
the optimization. The float loads are all indexed so there are no problems
|
||||
there. */
|
||||
|
||||
if (GET_CODE (mem) == SIGN_EXTEND && GET_MODE (XEXP (mem, 0)) == SImode)
|
||||
{
|
||||
if (!INT_REGNO_P (reg_regno))
|
||||
return;
|
||||
|
||||
mem_inner = XEXP (mem, 0);
|
||||
mem_mode = DImode;
|
||||
}
|
||||
|
||||
else if (GET_CODE (mem) == SIGN_EXTEND
|
||||
|| GET_CODE (mem) == ZERO_EXTEND
|
||||
|| GET_CODE (mem) == FLOAT_EXTEND)
|
||||
{
|
||||
mem_inner = XEXP (mem, 0);
|
||||
mem_mode = GET_MODE (mem_inner);
|
||||
}
|
||||
|
||||
if (!MEM_P (mem_inner))
|
||||
return;
|
||||
|
||||
/* Can we do PCREL_OPT for this reference? */
|
||||
if (!pcrel_opt_valid_mem_p (reg, mem_mode, mem_inner))
|
||||
return;
|
||||
|
||||
/* Allocate a new PC-relative label, and update the load external address
|
||||
insn.
|
||||
|
||||
If the register being loaded is different from the address register, we
|
||||
need to indicate both registers are set at the load of the address.
|
||||
|
||||
(parallel [(set (reg load)
|
||||
(unspec [(symbol_ref addr_symbol)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_ADDR))
|
||||
(set (reg addr)
|
||||
(unspec [(const_int 0)]
|
||||
UNSPEC_PCREL_OPT_LD_DATA))])
|
||||
|
||||
If the register being loaded is the same as the address register, we use
|
||||
an alternate form:
|
||||
|
||||
(set (reg load)
|
||||
(unspec [(symbol_ref addr_symbol)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG)) */
|
||||
unsigned int addr_regno = reg_or_subregno (addr_reg);
|
||||
rtx label_num = GEN_INT (++pcrel_opt_next_num);
|
||||
rtx reg_di = gen_rtx_REG (DImode, reg_regno);
|
||||
rtx addr_pattern;
|
||||
|
||||
/* Create the load address, either using the pattern with an explicit clobber
|
||||
if the address register is not the same as the register being loaded, or
|
||||
using the pattern that requires the address register to be the address
|
||||
loaded. */
|
||||
if (addr_regno != reg_regno)
|
||||
addr_pattern = gen_pcrel_opt_ld_addr (addr_reg, addr_symbol, label_num,
|
||||
reg_di);
|
||||
else
|
||||
addr_pattern = gen_pcrel_opt_ld_addr_same_reg (addr_reg, addr_symbol,
|
||||
label_num);
|
||||
|
||||
validate_change (addr_insn, &PATTERN (addr_insn), addr_pattern, false);
|
||||
|
||||
/* Update the load insn. If the mem had a sign/zero/float extend, add that
|
||||
also after doing the UNSPEC. Add an explicit clobber of the external
|
||||
address register just to make it clear that the address register dies.
|
||||
|
||||
(parallel [(set (reg:<MODE> data)
|
||||
(unspec:<MODE> [(mem (addr_reg)
|
||||
(reg:DI data)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_LD_RELOC))
|
||||
(clobber (reg:DI addr_reg))]) */
|
||||
rtvec v_load = gen_rtvec (3, mem_inner, reg_di, label_num);
|
||||
rtx new_load = gen_rtx_UNSPEC (GET_MODE (mem_inner), v_load,
|
||||
UNSPEC_PCREL_OPT_LD_RELOC);
|
||||
|
||||
if (GET_CODE (mem) != GET_CODE (mem_inner))
|
||||
new_load = gen_rtx_fmt_e (GET_CODE (mem), reg_mode, new_load);
|
||||
|
||||
rtx new_load_set = gen_rtx_SET (reg, new_load);
|
||||
rtx load_clobber = gen_rtx_CLOBBER (VOIDmode,
|
||||
(addr_regno == reg_regno
|
||||
? gen_rtx_SCRATCH (Pmode)
|
||||
: addr_reg));
|
||||
rtx new_load_pattern
|
||||
= gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, new_load_set, load_clobber));
|
||||
|
||||
validate_change (load_insn, &PATTERN (load_insn), new_load_pattern, false);
|
||||
|
||||
/* Attempt to apply the changes: */
|
||||
if (!apply_change_group ())
|
||||
{
|
||||
/* PCREL_OPT load optimization did not succeed. */
|
||||
counters.failed_loads++;
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"PCREL_OPT load failed (addr insn = %d, use insn = %d).\n",
|
||||
INSN_UID (addr_insn),
|
||||
INSN_UID (load_insn));
|
||||
return;
|
||||
}
|
||||
|
||||
/* PCREL_OPT load optimization succeeded. */
|
||||
counters.loads++;
|
||||
if (next_nonnote_insn (addr_insn) == load_insn)
|
||||
counters.adjacent_loads++;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"PCREL_OPT load (addr insn = %d, use insn = %d).\n",
|
||||
INSN_UID (addr_insn),
|
||||
INSN_UID (load_insn));
|
||||
|
||||
/* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
|
||||
after we have made changes to the insns. */
|
||||
df_analyze ();
|
||||
|
||||
}
|
||||
|
||||
/* Optimize a PC-relative load address to be used in a store. Before calling
|
||||
this function, pcrel_opt_address () uses DF to make sure it is safe to do
|
||||
the PCREL_OPT optimization.
|
||||
|
||||
Convert insns of the form:
|
||||
|
||||
(set (reg:DI addr)
|
||||
(symbol_ref:DI "ext_symbol"))
|
||||
|
||||
...
|
||||
|
||||
(set (mem:<MODE> (reg:DI addr))
|
||||
(reg:<MODE> value))
|
||||
|
||||
into:
|
||||
|
||||
(parallel [(set (reg:DI addr)
|
||||
(unspec:DI [(symbol_ref:DI "ext_symbol")
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_ST_ADDR))
|
||||
(use (reg:<MODE> value))])
|
||||
|
||||
...
|
||||
|
||||
(parallel [(set (mem:<MODE> (reg:DI addr))
|
||||
(unspec:<MODE> [(reg:<MODE>)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (reg:DI addr))])
|
||||
|
||||
The UNSPEC_PCREL_OPT_ST_ADDR insn will generate the load address plus a
|
||||
definition of a label (.Lpcrel<n>), while the UNSPEC_PCREL_OPT_ST_RELOC insn
|
||||
will generate the .reloc to tell the linker to tie the load address and load
|
||||
using that address together.
|
||||
|
||||
pld b,ext_symbol@got@pcrel
|
||||
.Lpcrel1:
|
||||
|
||||
...
|
||||
|
||||
.reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
|
||||
stw r,0(b)
|
||||
|
||||
If ext_symbol is defined in another object file in the main program and we
|
||||
are linking the main program, the linker will convert the above instructions
|
||||
to:
|
||||
|
||||
pstwz r,ext_symbol@got@pcrel
|
||||
|
||||
...
|
||||
|
||||
nop */
|
||||
|
||||
static void
|
||||
pcrel_opt_store (rtx_insn *addr_insn, /* insn loading address. */
|
||||
rtx_insn *store_insn) /* insn using address. */
|
||||
{
|
||||
rtx addr_old_set = PATTERN (addr_insn);
|
||||
gcc_assert (GET_CODE (addr_old_set) == SET);
|
||||
|
||||
rtx addr_reg = SET_DEST (addr_old_set);
|
||||
gcc_assert (base_reg_operand (addr_reg, Pmode));
|
||||
|
||||
rtx addr_symbol = SET_SRC (addr_old_set);
|
||||
gcc_assert (pcrel_external_address (addr_symbol, Pmode));
|
||||
|
||||
rtx store_set = PATTERN (store_insn);
|
||||
gcc_assert (GET_CODE (store_set) == SET);
|
||||
|
||||
rtx mem = SET_DEST (store_set);
|
||||
if (!MEM_P (mem))
|
||||
return;
|
||||
|
||||
machine_mode mem_mode = GET_MODE (mem);
|
||||
rtx reg = SET_SRC (store_set);
|
||||
|
||||
/* Don't allow storing the address of the external variable. */
|
||||
if (reg_or_subregno (reg) == reg_or_subregno (addr_reg))
|
||||
return;
|
||||
|
||||
/* Can we do PCREL_OPT for this reference? */
|
||||
if (!pcrel_opt_valid_mem_p (reg, mem_mode, mem))
|
||||
return;
|
||||
|
||||
/* Allocate a new PC-relative label, and update the load address insn.
|
||||
|
||||
(parallel [(set (reg addr)
|
||||
(unspec [(symbol_ref symbol)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_ST_ADDR))
|
||||
(use (reg store))])
|
||||
*/
|
||||
rtx label_num = GEN_INT (++pcrel_opt_next_num);
|
||||
rtvec v_addr = gen_rtvec (2, addr_symbol, label_num);
|
||||
rtx addr_unspec = gen_rtx_UNSPEC (Pmode, v_addr,
|
||||
UNSPEC_PCREL_OPT_ST_ADDR);
|
||||
rtx addr_new_set = gen_rtx_SET (addr_reg, addr_unspec);
|
||||
rtx addr_use = gen_rtx_USE (VOIDmode, reg);
|
||||
rtx addr_new_pattern
|
||||
= gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, addr_new_set, addr_use));
|
||||
|
||||
validate_change (addr_insn, &PATTERN (addr_insn), addr_new_pattern, false);
|
||||
|
||||
/* Update the store insn. Add an explicit clobber of the external address
|
||||
register just to be sure there are no additional uses of the address
|
||||
register.
|
||||
|
||||
(parallel [(set (mem (addr_reg)
|
||||
(unspec:<MODE> [(reg)
|
||||
(const_int label_num)]
|
||||
UNSPEC_PCREL_OPT_ST_RELOC))
|
||||
(clobber (reg:DI addr_reg))]) */
|
||||
rtvec v_store = gen_rtvec (2, reg, label_num);
|
||||
rtx new_store = gen_rtx_UNSPEC (mem_mode, v_store,
|
||||
UNSPEC_PCREL_OPT_ST_RELOC);
|
||||
|
||||
rtx new_store_set = gen_rtx_SET (mem, new_store);
|
||||
rtx store_clobber = gen_rtx_CLOBBER (VOIDmode, addr_reg);
|
||||
rtx new_store_pattern
|
||||
= gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, new_store_set, store_clobber));
|
||||
|
||||
validate_change (store_insn, &PATTERN (store_insn), new_store_pattern, false);
|
||||
|
||||
/* Attempt to apply the changes: */
|
||||
if (!apply_change_group ())
|
||||
{
|
||||
/* PCREL_OPT store failed. */
|
||||
counters.failed_stores++;
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"PCREL_OPT store failed (addr insn = %d, use insn = %d).\n",
|
||||
INSN_UID (addr_insn),
|
||||
INSN_UID (store_insn));
|
||||
return;
|
||||
}
|
||||
|
||||
/* PCREL_OPT store succeeded. */
|
||||
counters.stores++;
|
||||
if (next_nonnote_insn (addr_insn) == store_insn)
|
||||
counters.adjacent_stores++;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"PCREL_OPT store (addr insn = %d, use insn = %d).\n",
|
||||
INSN_UID (addr_insn),
|
||||
INSN_UID (store_insn));
|
||||
|
||||
/* Because we have set DF_DEFER_INSN_RESCAN, we have to explicitly do it
|
||||
after we have made changes to the insns. */
|
||||
df_analyze();
|
||||
|
||||
}
|
||||
|
||||
/* Return the register used as the base register of MEM, if the instruction has
|
||||
a pc-relative form. We look for BSWAP to rule out LFIWAX/LFIWZX/STFIWX, and
|
||||
ROTATE/VEC_SELECT are RTX_EXTRA not RTX_UNARY which rules out lxvd2x. This
|
||||
excludes instructions that do not have a pc-relative form. */
|
||||
|
||||
static rtx
|
||||
get_mem_base_reg (rtx mem)
|
||||
{
|
||||
const char * fmt;
|
||||
|
||||
while (!MEM_P (mem))
|
||||
{
|
||||
if (GET_RTX_CLASS (GET_CODE (mem)) != RTX_UNARY
|
||||
|| GET_CODE (mem) == BSWAP)
|
||||
return NULL_RTX;
|
||||
fmt = GET_RTX_FORMAT (GET_CODE (mem));
|
||||
if (fmt[0] != 'e')
|
||||
return NULL_RTX;
|
||||
mem = XEXP (mem, 0);
|
||||
if (mem == NULL_RTX )
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
if (!MEM_SIZE_KNOWN_P (mem))
|
||||
return NULL_RTX;
|
||||
|
||||
rtx addr_rtx = (XEXP (mem, 0));
|
||||
if (GET_CODE (addr_rtx) == PRE_MODIFY)
|
||||
addr_rtx = XEXP (addr_rtx, 1);
|
||||
|
||||
while (GET_CODE (addr_rtx) == PLUS
|
||||
&& CONST_INT_P (XEXP (addr_rtx, 1)))
|
||||
addr_rtx = XEXP (addr_rtx, 0);
|
||||
|
||||
if (!REG_P (addr_rtx))
|
||||
return NULL_RTX;
|
||||
|
||||
return addr_rtx;
|
||||
}
|
||||
|
||||
/* Check whether INSN contains a reference to REGNO that will inhibit the
|
||||
PCREL_OPT optimization. If TYPE is a load or store instruction, return true
|
||||
if there is a definition of REGNO. If TYPE is a load instruction, then
|
||||
return true of there is a use of REGNO. */
|
||||
|
||||
static bool
|
||||
insn_references_regno_p (rtx_insn *insn, unsigned int regno,
|
||||
enum attr_type type)
|
||||
{
|
||||
struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
|
||||
df_ref ref;
|
||||
|
||||
/* Return true if there is a definition of REGNO. */
|
||||
for (ref = DF_INSN_INFO_DEFS (insn_info); ref; ref = DF_REF_NEXT_LOC (ref))
|
||||
if (DF_REF_REGNO (ref) == regno)
|
||||
return true;
|
||||
|
||||
/* If type is a load, return true if there is a use of REGNO. */
|
||||
if (type == TYPE_LOAD
|
||||
|| type == TYPE_FPLOAD
|
||||
|| type == TYPE_VECLOAD)
|
||||
for (ref = DF_INSN_INFO_USES (insn_info); ref; ref = DF_REF_NEXT_LOC (ref))
|
||||
if (DF_REF_REGNO (ref) == regno)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Given an insn that loads up a base register with the address of an
|
||||
external symbol, see if we can optimize it with the PCREL_OPT
|
||||
optimization.
|
||||
|
||||
DF is used to make sure that there is exactly one definition and one
|
||||
non-debug use of the address register defined by the insn. The use insn must
|
||||
be a non-prefix insn, and must also be in the same basic block as the address
|
||||
insn.
|
||||
|
||||
ADDR_INSN is the insn that loads the external symbol address. */
|
||||
|
||||
static void
|
||||
pcrel_opt_address (rtx_insn *addr_insn)
|
||||
{
|
||||
counters.extern_addrs++;
|
||||
|
||||
/* Do some basic validation. */
|
||||
rtx addr_set = PATTERN (addr_insn);
|
||||
if (GET_CODE (addr_set) != SET)
|
||||
return;
|
||||
|
||||
rtx addr_reg = SET_DEST (addr_set);
|
||||
rtx addr_symbol = SET_SRC (addr_set);
|
||||
|
||||
if (!base_reg_operand (addr_reg, Pmode)
|
||||
|| !pcrel_external_address (addr_symbol, Pmode))
|
||||
return;
|
||||
|
||||
/* The address register must have exactly one definition. */
|
||||
struct df_insn_info *insn_info = DF_INSN_INFO_GET (addr_insn);
|
||||
if (!insn_info)
|
||||
return;
|
||||
|
||||
df_ref def = df_single_def (insn_info);
|
||||
if (!def)
|
||||
return;
|
||||
|
||||
/* Make sure there is at least one use. */
|
||||
df_link *chain = DF_REF_CHAIN (def);
|
||||
if (!chain || !chain->ref)
|
||||
return;
|
||||
|
||||
/* Get the insn of the possible load or store. */
|
||||
rtx_insn *use_insn = DF_REF_INSN (chain->ref);
|
||||
|
||||
/* Ensure there are no other uses. */
|
||||
for (chain = chain->next; chain; chain = chain->next)
|
||||
if (chain->ref && DF_REF_INSN_INFO (chain->ref))
|
||||
{
|
||||
gcc_assert (DF_REF_INSN (chain->ref));
|
||||
if (NONDEBUG_INSN_P (DF_REF_INSN (chain->ref)))
|
||||
return;
|
||||
}
|
||||
|
||||
/* The use instruction must be a single non-prefixed instruction. */
|
||||
if (get_attr_length (use_insn) != 4)
|
||||
return;
|
||||
|
||||
/* The address and the memory operation must be in the same basic block. */
|
||||
if (BLOCK_FOR_INSN (use_insn) != BLOCK_FOR_INSN (addr_insn))
|
||||
return;
|
||||
|
||||
/* If this isn't a simple SET, skip doing the optimization. */
|
||||
if (GET_CODE (PATTERN (use_insn)) != SET)
|
||||
return;
|
||||
|
||||
enum attr_type use_insn_type = get_attr_type (use_insn);
|
||||
unsigned int use_regno;
|
||||
|
||||
/* Make sure the use_insn is using addr_reg as its base register
|
||||
for the load or store, and determine the regno for the register
|
||||
used in the use_insn. */
|
||||
rtx use_dest, use_src;
|
||||
switch (use_insn_type)
|
||||
{
|
||||
case TYPE_LOAD:
|
||||
case TYPE_FPLOAD:
|
||||
case TYPE_VECLOAD:
|
||||
/* Make sure our address register is the same register used in the
|
||||
base address of the load. */
|
||||
if (addr_reg != get_mem_base_reg (SET_SRC (PATTERN (use_insn))))
|
||||
return;
|
||||
/* Make sure we are setting a register before we look at REGNO. */
|
||||
use_dest = SET_DEST (PATTERN (use_insn));
|
||||
if (!register_operand (use_dest, GET_MODE (use_dest)))
|
||||
return;
|
||||
use_regno = REGNO (use_dest);
|
||||
break;
|
||||
case TYPE_STORE:
|
||||
case TYPE_FPSTORE:
|
||||
case TYPE_VECSTORE:
|
||||
/* Make sure our address register is the same register used in the
|
||||
base address of the store. */
|
||||
if (addr_reg != get_mem_base_reg (SET_DEST (PATTERN (use_insn))))
|
||||
return;
|
||||
/* Make sure this is a register before we look at REGNO. */
|
||||
use_src = SET_SRC (PATTERN (use_insn));
|
||||
if (!register_operand (use_src, GET_MODE (use_src)))
|
||||
return;
|
||||
use_regno = REGNO (use_src);
|
||||
break;
|
||||
default:
|
||||
/* We can only optimize loads and stores. Ignore everything else. */
|
||||
return;
|
||||
}
|
||||
|
||||
rtx_insn *insn;
|
||||
for (insn = NEXT_INSN (addr_insn);
|
||||
insn != use_insn;
|
||||
insn = NEXT_INSN (insn))
|
||||
{
|
||||
/* If we see a call, do not do the PCREL_OPT optimization. */
|
||||
if (CALL_P (insn))
|
||||
return;
|
||||
|
||||
/* Skip debug insns. */
|
||||
if (!NONDEBUG_INSN_P (insn))
|
||||
continue;
|
||||
|
||||
/* See if it is a load or store. */
|
||||
if (GET_CODE (PATTERN (insn)) != USE
|
||||
&& GET_CODE (PATTERN (insn)) != CLOBBER)
|
||||
{
|
||||
switch (get_attr_type (insn))
|
||||
{
|
||||
case TYPE_LOAD:
|
||||
/* While load of the external address is a 'load' for scheduling
|
||||
purposes, it should be safe to allow loading other external
|
||||
addresses between the load of the external address we are
|
||||
currently looking at and the load or store using that
|
||||
address. */
|
||||
if (get_attr_loads_external_address (insn)
|
||||
== LOADS_EXTERNAL_ADDRESS_YES)
|
||||
break;
|
||||
/* fall through */
|
||||
|
||||
case TYPE_FPLOAD:
|
||||
case TYPE_VECLOAD:
|
||||
/* Don't do the PCREL_OPT store optimization if there is a load
|
||||
operation. For example, the load might be trying to load the
|
||||
value being stored in between getting the address and doing
|
||||
the store. */
|
||||
if (use_insn_type == TYPE_STORE
|
||||
|| use_insn_type == TYPE_FPSTORE
|
||||
|| use_insn_type == TYPE_VECSTORE)
|
||||
return;
|
||||
break;
|
||||
|
||||
case TYPE_STORE:
|
||||
case TYPE_FPSTORE:
|
||||
case TYPE_VECSTORE:
|
||||
/* Don't do the PCREL_OPT load optimization if there is a store
|
||||
operation. Perhaps the store might be to the global variable
|
||||
through a pointer. */
|
||||
return;
|
||||
|
||||
case TYPE_LOAD_L:
|
||||
case TYPE_STORE_C:
|
||||
case TYPE_HTM:
|
||||
case TYPE_HTMSIMPLE:
|
||||
/* Don't do the optimization through atomic operations. */
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for invalid references of the non-address register that is
|
||||
used in the load or store instruction. */
|
||||
if (insn_references_regno_p (insn, use_regno, use_insn_type))
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is this a load or a store? */
|
||||
switch (use_insn_type)
|
||||
{
|
||||
case TYPE_LOAD:
|
||||
case TYPE_FPLOAD:
|
||||
case TYPE_VECLOAD:
|
||||
pcrel_opt_load (addr_insn, use_insn);
|
||||
break;
|
||||
|
||||
case TYPE_STORE:
|
||||
case TYPE_FPSTORE:
|
||||
case TYPE_VECSTORE:
|
||||
pcrel_opt_store (addr_insn, use_insn);
|
||||
break;
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Optimize pcrel external variable references. */
|
||||
|
||||
static unsigned int
|
||||
pcrel_opt_pass (function *fun)
|
||||
{
|
||||
basic_block bb;
|
||||
rtx_insn *insn, *curr_insn = 0;
|
||||
|
||||
memset (&counters, 0, sizeof (counters));
|
||||
|
||||
/* Dataflow analysis for use-def chains. However we have to specify both UD
|
||||
and DU as otherwise when we make changes to insns for the PCREL_OPT there
|
||||
will be dangling references. */
|
||||
df_set_flags (DF_RD_PRUNE_DEAD_DEFS);
|
||||
df_chain_add_problem (DF_DU_CHAIN + DF_UD_CHAIN);
|
||||
df_note_add_problem ();
|
||||
df_analyze ();
|
||||
|
||||
/* Set the defer flag as our pattern of operation will be to modify two insns,
|
||||
then call df_analyze (). */
|
||||
df_set_flags (DF_DEFER_INSN_RESCAN | DF_LR_RUN_DCE);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "\n");
|
||||
|
||||
/* Look at each basic block to see if there is a load of an external
|
||||
variable's external address, and a single load/store using that external
|
||||
address. */
|
||||
FOR_ALL_BB_FN (bb, fun)
|
||||
{
|
||||
FOR_BB_INSNS_SAFE (bb, insn, curr_insn)
|
||||
{
|
||||
if (NONJUMP_INSN_P (insn)
|
||||
&& single_set (insn)
|
||||
&& get_attr_loads_external_address (insn)
|
||||
== LOADS_EXTERNAL_ADDRESS_YES)
|
||||
pcrel_opt_address (insn);
|
||||
}
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file,
|
||||
"\n# of loads of an address of an external symbol = %lu\n",
|
||||
counters.extern_addrs);
|
||||
|
||||
fprintf (dump_file, "# of PCREL_OPT loads = %lu (adjacent %lu)\n",
|
||||
counters.loads, counters.adjacent_loads);
|
||||
|
||||
if (counters.failed_loads)
|
||||
fprintf (dump_file, "# of failed PCREL_OPT loads = %lu\n",
|
||||
counters.failed_loads);
|
||||
|
||||
fprintf (dump_file, "# of PCREL_OPT stores = %lu (adjacent %lu)\n",
|
||||
counters.stores, counters.adjacent_stores);
|
||||
|
||||
if (counters.failed_stores)
|
||||
fprintf (dump_file, "# of failed PCREL_OPT stores = %lu\n",
|
||||
counters.failed_stores);
|
||||
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
df_remove_problem (df_chain);
|
||||
df_process_deferred_rescans ();
|
||||
df_set_flags (DF_RD_PRUNE_DEAD_DEFS | DF_LR_RUN_DCE);
|
||||
df_analyze ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Optimize pc-relative references for the new PCREL_OPT pass. */
|
||||
const pass_data pass_data_pcrel_opt =
|
||||
{
|
||||
RTL_PASS, /* type. */
|
||||
"pcrel_opt", /* name. */
|
||||
OPTGROUP_NONE, /* optinfo_flags. */
|
||||
TV_NONE, /* tv_id. */
|
||||
0, /* properties_required. */
|
||||
0, /* properties_provided. */
|
||||
0, /* properties_destroyed. */
|
||||
0, /* todo_flags_start. */
|
||||
TODO_df_finish, /* todo_flags_finish. */
|
||||
};
|
||||
|
||||
/* Pass data structures. */
|
||||
class pcrel_opt : public rtl_opt_pass
|
||||
{
|
||||
public:
|
||||
pcrel_opt (gcc::context *ctxt)
|
||||
: rtl_opt_pass (pass_data_pcrel_opt, ctxt)
|
||||
{}
|
||||
|
||||
~pcrel_opt (void)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *)
|
||||
{
|
||||
return (TARGET_PCREL && TARGET_PCREL_OPT && optimize);
|
||||
}
|
||||
|
||||
virtual unsigned int execute (function *fun)
|
||||
{
|
||||
return pcrel_opt_pass (fun);
|
||||
}
|
||||
|
||||
opt_pass *clone ()
|
||||
{
|
||||
return new pcrel_opt (m_ctxt);
|
||||
}
|
||||
};
|
||||
|
||||
rtl_opt_pass *
|
||||
make_pass_pcrel_opt (gcc::context *ctxt)
|
||||
{
|
||||
return new pcrel_opt (ctxt);
|
||||
}
|
@ -193,10 +193,13 @@ extern enum insn_form address_to_insn_form (rtx, machine_mode,
|
||||
enum non_prefixed_form);
|
||||
extern bool address_is_non_pfx_d_or_x (rtx addr, machine_mode mode,
|
||||
enum non_prefixed_form non_prefix_format);
|
||||
extern bool pcrel_opt_valid_mem_p (rtx, machine_mode, rtx);
|
||||
enum non_prefixed_form reg_to_non_prefixed (rtx reg, machine_mode mode);
|
||||
extern bool prefixed_load_p (rtx_insn *);
|
||||
extern bool prefixed_store_p (rtx_insn *);
|
||||
extern bool prefixed_paddi_p (rtx_insn *);
|
||||
extern void rs6000_asm_output_opcode (FILE *);
|
||||
extern void output_pcrel_opt_reloc (rtx);
|
||||
extern void rs6000_final_prescan_insn (rtx_insn *, rtx [], int);
|
||||
extern int rs6000_adjust_insn_length (rtx_insn *, int);
|
||||
|
||||
@ -309,6 +312,7 @@ namespace gcc { class context; }
|
||||
class rtl_opt_pass;
|
||||
|
||||
extern rtl_opt_pass *make_pass_analyze_swaps (gcc::context *);
|
||||
extern rtl_opt_pass *make_pass_pcrel_opt (gcc::context *);
|
||||
extern bool rs6000_sum_of_two_registers_p (const_rtx expr);
|
||||
extern bool rs6000_quadword_masked_address_p (const_rtx exp);
|
||||
extern rtx rs6000_gen_lvx (enum machine_mode, rtx, rtx);
|
||||
|
@ -1173,7 +1173,6 @@ static bool rs6000_secondary_reload_move (enum rs6000_reg_type,
|
||||
machine_mode,
|
||||
secondary_reload_info *,
|
||||
bool);
|
||||
static enum non_prefixed_form reg_to_non_prefixed (rtx reg, machine_mode mode);
|
||||
rtl_opt_pass *make_pass_analyze_swaps (gcc::context*);
|
||||
|
||||
/* Hash table stuff for keeping track of TOC entries. */
|
||||
@ -4452,6 +4451,9 @@ rs6000_option_override_internal (bool global_init_p)
|
||||
rs6000_isa_flags &= ~OPTION_MASK_MMA;
|
||||
}
|
||||
|
||||
if (!TARGET_PCREL && TARGET_PCREL_OPT)
|
||||
rs6000_isa_flags &= ~OPTION_MASK_PCREL_OPT;
|
||||
|
||||
if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET)
|
||||
rs6000_print_isa_options (stderr, 0, "after subtarget", rs6000_isa_flags);
|
||||
|
||||
@ -8985,8 +8987,57 @@ rs6000_delegitimize_address (rtx orig_x)
|
||||
{
|
||||
rtx x, y, offset;
|
||||
|
||||
if (GET_CODE (orig_x) == UNSPEC && XINT (orig_x, 1) == UNSPEC_FUSION_GPR)
|
||||
orig_x = XVECEXP (orig_x, 0, 0);
|
||||
/* UNSPEC_FUSION_GPR is created by the peephole2 for power8 fusion. It
|
||||
encodes loading up the high part of the address of a TOC reference along
|
||||
with a load of a GPR using the same base register used for the load. We
|
||||
return the original SYMBOL_REF.
|
||||
|
||||
(set (reg:INT1 <reg>
|
||||
(unspec:INT1 [<combined-address>] UNSPEC_FUSION_GPR)))
|
||||
|
||||
UNSPEC_PCREL_OPT_LD_ADDR is used by the power10 PCREL_OPT pass. These
|
||||
UNSPECs include the external SYMBOL_REF along with the value being loaded.
|
||||
We return the original SYMBOL_REF.
|
||||
|
||||
(parallel [(set (reg:DI <base-reg>)
|
||||
(unspec:DI [(symbol_ref <symbol>)
|
||||
(const_int <marker>)]
|
||||
UNSPEC_PCREL_OPT_LD_ADDR))
|
||||
(set (reg:DI <load-reg>)
|
||||
(unspec:DI [(const_int 0)]
|
||||
UNSPEC_PCREL_OPT_LD_DATA))])
|
||||
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG is an alternative that is used if the
|
||||
GPR being loaded is the same as the GPR used to hold the external address.
|
||||
|
||||
(set (reg:DI <base-reg>)
|
||||
(unspec:DI [(symbol_ref <symbol>)
|
||||
(const_int <marker>)]
|
||||
UNSPEC_PCREL_OPT_LD_SAME_REG))
|
||||
|
||||
UNSPEC_PCREL_OPT_ST_ADDR is used by the power10 PCREL_OPT pass. This
|
||||
UNSPEC include the external SYMBOL_REF along with the value being loaded.
|
||||
We return the original SYMBOL_REF.
|
||||
|
||||
(parallel [(set (reg:DI <base-reg>)
|
||||
(unspec:DI [(symbol_ref <symbol>)
|
||||
(const_int <marker>)]
|
||||
UNSPEC_PCREL_OPT_ST_ADDR))
|
||||
(use (reg <store-reg>))]) */
|
||||
|
||||
if (GET_CODE (orig_x) == UNSPEC)
|
||||
switch (XINT (orig_x, 1))
|
||||
{
|
||||
case UNSPEC_FUSION_GPR:
|
||||
case UNSPEC_PCREL_OPT_LD_ADDR:
|
||||
case UNSPEC_PCREL_OPT_LD_SAME_REG:
|
||||
case UNSPEC_PCREL_OPT_ST_ADDR:
|
||||
orig_x = XVECEXP (orig_x, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
orig_x = delegitimize_mem_from_attrs (orig_x);
|
||||
|
||||
@ -23788,6 +23839,7 @@ static struct rs6000_opt_mask const rs6000_opt_masks[] =
|
||||
{ "mulhw", OPTION_MASK_MULHW, false, true },
|
||||
{ "multiple", OPTION_MASK_MULTIPLE, false, true },
|
||||
{ "pcrel", OPTION_MASK_PCREL, false, true },
|
||||
{ "pcrel-opt", OPTION_MASK_PCREL_OPT, false, true },
|
||||
{ "popcntb", OPTION_MASK_POPCNTB, false, true },
|
||||
{ "popcntd", OPTION_MASK_POPCNTD, false, true },
|
||||
{ "power8-fusion", OPTION_MASK_P8_FUSION, false, true },
|
||||
@ -25932,6 +25984,32 @@ address_is_non_pfx_d_or_x (rtx addr, machine_mode mode,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if an REG with a given MODE is loaded from or stored into a MEM
|
||||
location uses a non-prefixed D/DS/DQ-form address. This is used to validate
|
||||
the load or store with the PCREL_OPT optimization to make sure it is an
|
||||
instruction that can be optimized.
|
||||
|
||||
We need to specify the MODE separately from the REG to allow for loads that
|
||||
include zero/sign/float extension. */
|
||||
|
||||
bool
|
||||
pcrel_opt_valid_mem_p (rtx reg, machine_mode mode, rtx mem)
|
||||
{
|
||||
/* If the instruction is indexed only like LFIWAX/LXSIWAX we cannot do the
|
||||
PCREL_OPT optimization. */
|
||||
enum non_prefixed_form non_prefixed = reg_to_non_prefixed (reg, mode);
|
||||
if (non_prefixed == NON_PREFIXED_X)
|
||||
return false;
|
||||
|
||||
/* Check if this is a non-prefixed D/DS/DQ-form instruction. */
|
||||
rtx addr = XEXP (mem, 0);
|
||||
enum insn_form iform = address_to_insn_form (addr, mode, non_prefixed);
|
||||
return (iform == INSN_FORM_BASE_REG
|
||||
|| iform == INSN_FORM_D
|
||||
|| iform == INSN_FORM_DS
|
||||
|| iform == INSN_FORM_DQ);
|
||||
}
|
||||
|
||||
/* Helper function to see if we're potentially looking at lfs/stfs.
|
||||
- PARALLEL containing a SET and a CLOBBER
|
||||
- stfs:
|
||||
@ -25990,7 +26068,7 @@ is_lfs_stfs_insn (rtx_insn *insn)
|
||||
/* Helper function to take a REG and a MODE and turn it into the non-prefixed
|
||||
instruction format (D/DS/DQ) used for offset memory. */
|
||||
|
||||
static enum non_prefixed_form
|
||||
enum non_prefixed_form
|
||||
reg_to_non_prefixed (rtx reg, machine_mode mode)
|
||||
{
|
||||
/* If it isn't a register, use the defaults. */
|
||||
@ -26213,11 +26291,34 @@ void
|
||||
rs6000_asm_output_opcode (FILE *stream)
|
||||
{
|
||||
if (prepend_p_to_next_insn)
|
||||
fprintf (stream, "p");
|
||||
{
|
||||
fprintf (stream, "p");
|
||||
|
||||
/* Reset the flag in the case where there are separate insn lines in the
|
||||
sequence, so the 'p' is only emitted for the first line. This shows up
|
||||
when we are doing the PCREL_OPT optimization, in that the label created
|
||||
with %r<n> would have a leading 'p' printed. */
|
||||
prepend_p_to_next_insn = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emit the relocation to tie the next instruction to a previous instruction
|
||||
that loads up an external address. This is used to do the PCREL_OPT
|
||||
optimization. Note, the label is generated after the PLD of the got
|
||||
pc-relative address to allow for the assembler to insert NOPs before the PLD
|
||||
instruction. The operand is a constant integer that is the label
|
||||
number. */
|
||||
|
||||
void
|
||||
output_pcrel_opt_reloc (rtx label_num)
|
||||
{
|
||||
rtx operands[1] = { label_num };
|
||||
output_asm_insn (".reloc .Lpcrel%0-8,R_PPC64_PCREL_OPT,.-(.Lpcrel%0-8)",
|
||||
operands);
|
||||
}
|
||||
|
||||
/* Adjust the length of an INSN. LENGTH is the currently-computed length and
|
||||
should be adjusted to reflect any required changes. This macro is used when
|
||||
there is some systematic length adjustment required that would be difficult
|
||||
|
@ -292,6 +292,10 @@
|
||||
|
||||
(const_string "no")))
|
||||
|
||||
;; Whether an insn loads an external address for the PCREL_OPT optimizaton.
|
||||
(define_attr "loads_external_address" "no,yes"
|
||||
(const_string "no"))
|
||||
|
||||
;; Return the number of real hardware instructions in a combined insn. If it
|
||||
;; is 0, just use the length / 4.
|
||||
(define_attr "num_insns" "" (const_int 0))
|
||||
@ -10261,7 +10265,8 @@
|
||||
"TARGET_PCREL"
|
||||
"ld %0,%a1"
|
||||
[(set_attr "prefixed" "yes")
|
||||
(set_attr "type" "load")])
|
||||
(set_attr "type" "load")
|
||||
(set_attr "loads_external_address" "yes")])
|
||||
|
||||
;; TOC register handling.
|
||||
|
||||
@ -14946,3 +14951,4 @@
|
||||
(include "crypto.md")
|
||||
(include "htm.md")
|
||||
(include "fusion.md")
|
||||
(include "pcrel-opt.md")
|
||||
|
@ -609,6 +609,10 @@ mpcrel
|
||||
Target Mask(PCREL) Var(rs6000_isa_flags)
|
||||
Generate (do not generate) pc-relative memory addressing.
|
||||
|
||||
mpcrel-opt
|
||||
Target Undocumented Mask(PCREL_OPT) Var(rs6000_isa_flags)
|
||||
Generate (do not generate) pc-relative memory optimizations for externals.
|
||||
|
||||
mmma
|
||||
Target Mask(MMA) Var(rs6000_isa_flags)
|
||||
Generate (do not generate) MMA instructions.
|
||||
|
@ -23,6 +23,10 @@ TM_H += $(srcdir)/config/rs6000/rs6000-cpus.def
|
||||
TM_H += $(srcdir)/config/rs6000/rs6000-modes.h
|
||||
PASSES_EXTRA += $(srcdir)/config/rs6000/rs6000-passes.def
|
||||
|
||||
rs6000-pcrel-opt.o: $(srcdir)/config/rs6000/rs6000-pcrel-opt.c
|
||||
$(COMPILE) $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
rs6000-c.o: $(srcdir)/config/rs6000/rs6000-c.c
|
||||
$(COMPILE) $<
|
||||
$(POSTCOMPILE)
|
||||
@ -90,4 +94,5 @@ MD_INCLUDES = $(srcdir)/config/rs6000/rs64.md \
|
||||
$(srcdir)/config/rs6000/crypto.md \
|
||||
$(srcdir)/config/rs6000/htm.md \
|
||||
$(srcdir)/config/rs6000/dfp.md \
|
||||
$(srcdir)/config/rs6000/fusion.md
|
||||
$(srcdir)/config/rs6000/fusion.md \
|
||||
$(srcdir)/config/rs6000/pcrel-opt.md
|
||||
|
17
gcc/testsuite/gcc.target/powerpc/pcrel-opt-inc-di.c
Normal file
17
gcc/testsuite/gcc.target/powerpc/pcrel-opt-inc-di.c
Normal file
@ -0,0 +1,17 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE unsigned int
|
||||
|
||||
/* Test whether using an external variable twice (doing an increment) prevents
|
||||
the PCREL_OPT optimization. */
|
||||
extern TYPE ext;
|
||||
|
||||
void
|
||||
inc (void)
|
||||
{
|
||||
ext++; /* No PCREL_OPT (uses address twice). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "R_PPC64_PCREL_OPT" } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-df.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-df.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE double
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
double. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-di.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-di.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE long long
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for long
|
||||
long. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
double
|
||||
get_double (void)
|
||||
{
|
||||
return (double) ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 3 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-hi.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-hi.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE unsigned short
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for unsigned
|
||||
short. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
double
|
||||
get_double (void)
|
||||
{
|
||||
return (double) ext[0]; /* No PCREL_OPT (LXSIHZX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-qi.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-qi.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE unsigned char
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for unsigned
|
||||
char. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
double
|
||||
get_double (void)
|
||||
{
|
||||
return (double) ext[0]; /* No PCREL_OPT (LXSIBZX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-sf.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-sf.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE float
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
float. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
double
|
||||
get_double (void)
|
||||
{
|
||||
return (double) ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 3 } } */
|
41
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-si.c
Normal file
41
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-si.c
Normal file
@ -0,0 +1,41 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE int
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for int. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
double
|
||||
get_double (void)
|
||||
{
|
||||
return (double) ext[0]; /* No PCREL_OPT (LFIWAX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-vector.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-ld-vector.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE vector double
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
vector double. */
|
||||
extern TYPE ext[];
|
||||
|
||||
TYPE
|
||||
get (void)
|
||||
{
|
||||
return ext[0]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get2 (void)
|
||||
{
|
||||
return ext[2]; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_large (void)
|
||||
{
|
||||
return ext[LARGE]; /* No PCREL_OPT (load is prefixed). */
|
||||
}
|
||||
|
||||
TYPE
|
||||
get_variable (unsigned long n)
|
||||
{
|
||||
return ext[n]; /* No PCREL_OPT (load is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-df.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-df.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE double
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
double. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-di.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-di.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE long long
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for long
|
||||
long. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-hi.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-hi.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE unsigned short
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for unsigned
|
||||
short. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_double (double a)
|
||||
{
|
||||
ext[0] = (TYPE) a; /* No PCREL_OPT (STXIHZX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-qi.c
Normal file
42
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-qi.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE unsigned char
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for unsigned
|
||||
char. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_double (double a)
|
||||
{
|
||||
ext[0] = (TYPE) a; /* No PCREL_OPT (STXIBZX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-sf.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-sf.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE float
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
float. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
41
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-si.c
Normal file
41
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-si.c
Normal file
@ -0,0 +1,41 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE int
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for int. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_double (double a)
|
||||
{
|
||||
ext[0] = (TYPE) a; /* No PCREL_OPT (STFIWX is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-vector.c
Normal file
36
gcc/testsuite/gcc.target/powerpc/pcrel-opt-st-vector.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-require-effective-target powerpc_pcrel } */
|
||||
/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
|
||||
|
||||
#define TYPE vector double
|
||||
#define LARGE 0x20000
|
||||
|
||||
/* Test whether we get the right number of PCREL_OPT optimizations for
|
||||
vector double. */
|
||||
extern TYPE ext[];
|
||||
|
||||
void
|
||||
store (TYPE a)
|
||||
{
|
||||
ext[0] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store2 (TYPE a)
|
||||
{
|
||||
ext[2] = a; /* PCREL_OPT relocation. */
|
||||
}
|
||||
|
||||
void
|
||||
store_large (TYPE a)
|
||||
{
|
||||
ext[LARGE] = a; /* No PCREL_OPT (store is prefixed). */
|
||||
}
|
||||
|
||||
void
|
||||
store_variable (TYPE a, unsigned long n)
|
||||
{
|
||||
ext[n] = a; /* No PCREL_OPT (store is indexed). */
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
|
Loading…
x
Reference in New Issue
Block a user