aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meissner <meissner@linux.ibm.com>2019-10-08 23:58:06 +0000
committerMichael Meissner <meissner@linux.ibm.com>2019-10-08 23:58:06 +0000
commit17eca85df8eca7da97dd8022d0fdfc2ab211d0bb (patch)
treead01a2fb1a74678b4ef0588f23c4b73f74ac9dc3
parentd484ef60b38cb94f1728b8117fa41fbf47e4df52 (diff)
Add PCREL_OPT.
[gcc] 2019-10-08 Michael Meissner <meissner@linux.ibm.com> Alan Modra <amodra@gmail.com> * config.gcc (powerpc*-*-*): Add rs6000-pcrel.o. (rs6000*-*-*): Add rs6000-pcrel.o. * config/rs6000/pcrel.md: New file. * config/rs6000/predicates.md (one_reg_memory_operand): New predicate. (unspec_tls): Add support for PC-relative TLS relocations. (pcrel_external_memory): New predicate. * config/rs6000/rs6000-cpus.def (ADDRESSING_FUTURE_MASKS): Add -mpcrel-opt. (POWERPC_MASKS): Add -mpcrel-opt. * config/rs6000/rs6000-passes.def: Add pcrel_opt pass. * config/rs6000/rs6000-pcrel.c: New file. * config/rs6000/rs6000-protos.h (make_pass_pcrel_opt): New declaration. * config/rs6000/rs6000.c (rs6000_option_override_internal): Add support for -mpcrel-opt. Add support for PC-relative TLS symbols. (rs6000_legitimize_tls_address): Add support for PC-relative TLS symbols. (rs6000_opt_masks): Add -mpcrel-opt. (pcrel_opt_label_num): New static state variable for PCREL_OPT optimization. (rs6000_final_prescan_insn): Add support for PCREL_OPT optimization. (rs6000_asm_output_opcode): Add support for PCREL_OPT optimization. * config/rs6000/rs6000.md: Include pcrel.md. (UNSPEC_TLSTLS_PCREL): New unspec. (pcrel_opt insn attribute): New attribute for PCREL_OPT. (tls_gd_pcrel<bits>): New insn for PC-relative TLS support. (tls_ld_pcrel<bits>): New insn for PC-relative TLS support. (tls_ld<bits>): Set prefixed insn attribute. (tls_tprel_<bits>): Set prefixed insn attribute. (tls_got_tprel_pcrel_<bits>): New insn for PC-relative TLS support. (tls_tls_pcrel_<bits>): New insn for PC-relative TLS support. * config/rs6000/rs6000.opt (-mpcrel-opt): New switch. * config/rs6000/t-rs6000 (rs6000-pcrel.o): New object file. (MD_INCLUDES): Add pcrel.md. [gcc/testsuite] 2019-10-08 Michael Meissner <meissner@linux.ibm.com> * gcc.target/powerpc/pcrel-opt-di.c: New tests for PCREL_OPT. * gcc.target/powerpc/pcrel-opt-di2.c: Likewise. * gcc.target/powerpc/pcrel-opt-di3.c: Likewise. git-svn-id: https://gcc.gnu.org/svn/gcc/branches/ibm/pcrel-peter@276746 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog.meissner43
-rw-r--r--gcc/config.gcc14
-rw-r--r--gcc/config/rs6000/pcrel.md598
-rw-r--r--gcc/config/rs6000/predicates.md20
-rw-r--r--gcc/config/rs6000/rs6000-cpus.def2
-rw-r--r--gcc/config/rs6000/rs6000-passes.def9
-rw-r--r--gcc/config/rs6000/rs6000-pcrel.c497
-rw-r--r--gcc/config/rs6000/rs6000-protos.h1
-rw-r--r--gcc/config/rs6000/rs6000.c86
-rw-r--r--gcc/config/rs6000/rs6000.md73
-rw-r--r--gcc/config/rs6000/rs6000.opt4
-rw-r--r--gcc/config/rs6000/t-rs60005
-rw-r--r--gcc/testsuite/ChangeLog.meissner6
-rw-r--r--gcc/testsuite/gcc.target/powerpc/pcrel-opt-di.c53
-rw-r--r--gcc/testsuite/gcc.target/powerpc/pcrel-opt-di2.c56
-rw-r--r--gcc/testsuite/gcc.target/powerpc/pcrel-opt-di3.c36
16 files changed, 1486 insertions, 17 deletions
diff --git a/gcc/ChangeLog.meissner b/gcc/ChangeLog.meissner
index 9e0d9bced53..b37a7377297 100644
--- a/gcc/ChangeLog.meissner
+++ b/gcc/ChangeLog.meissner
@@ -1,4 +1,47 @@
2019-10-08 Michael Meissner <meissner@linux.ibm.com>
+ Alan Modra <amodra@gmail.com>
+
+ * config.gcc (powerpc*-*-*): Add rs6000-pcrel.o.
+ (rs6000*-*-*): Add rs6000-pcrel.o.
+ * config/rs6000/pcrel.md: New file.
+ * config/rs6000/predicates.md (one_reg_memory_operand): New
+ predicate.
+ (unspec_tls): Add support for PC-relative TLS relocations.
+ (pcrel_external_memory): New predicate.
+ * config/rs6000/rs6000-cpus.def (ADDRESSING_FUTURE_MASKS): Add
+ -mpcrel-opt.
+ (POWERPC_MASKS): Add -mpcrel-opt.
+ * config/rs6000/rs6000-passes.def: Add pcrel_opt pass.
+ * config/rs6000/rs6000-pcrel.c: New file.
+ * config/rs6000/rs6000-protos.h (make_pass_pcrel_opt): New
+ declaration.
+ * config/rs6000/rs6000.c (rs6000_option_override_internal): Add
+ support for -mpcrel-opt. Add support for PC-relative TLS
+ symbols.
+ (rs6000_legitimize_tls_address): Add support for PC-relative TLS
+ symbols.
+ (rs6000_opt_masks): Add -mpcrel-opt.
+ (pcrel_opt_label_num): New static state variable for PCREL_OPT
+ optimization.
+ (rs6000_final_prescan_insn): Add support for PCREL_OPT
+ optimization.
+ (rs6000_asm_output_opcode): Add support for PCREL_OPT
+ optimization.
+ * config/rs6000/rs6000.md: Include pcrel.md.
+ (UNSPEC_TLSTLS_PCREL): New unspec.
+ (pcrel_opt insn attribute): New attribute for PCREL_OPT.
+ (tls_gd_pcrel<bits>): New insn for PC-relative TLS support.
+ (tls_ld_pcrel<bits>): New insn for PC-relative TLS support.
+ (tls_ld<bits>): Set prefixed insn attribute.
+ (tls_tprel_<bits>): Set prefixed insn attribute.
+ (tls_got_tprel_pcrel_<bits>): New insn for PC-relative TLS
+ support.
+ (tls_tls_pcrel_<bits>): New insn for PC-relative TLS support.
+ * config/rs6000/rs6000.opt (-mpcrel-opt): New switch.
+ * config/rs6000/t-rs6000 (rs6000-pcrel.o): New object file.
+ (MD_INCLUDES): Add pcrel.md.
+
+2019-10-08 Michael Meissner <meissner@linux.ibm.com>
* config/rs6000/rs6000-string.c (expand_block_move): Use vector
pair instructions if they are available for block moves. If
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 511aeb454bf..727d0a4f258 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -502,7 +502,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.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"
@@ -516,7 +517,9 @@ 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-logue.c"
+ target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-call.c"
+ target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel.c"
;;
pru-*-*)
cpu_type=pru
@@ -528,8 +531,11 @@ 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"
- target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c"
+ extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o"
+ extra_objs="${extra_objs} rs6000-call.o rs6000-pcrel.o"
+ target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c"
+ target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-call.c"
+ target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel.c"
;;
sparc*-*-*)
cpu_type=sparc
diff --git a/gcc/config/rs6000/pcrel.md b/gcc/config/rs6000/pcrel.md
index e69de29bb2d..fd9cf85f20b 100644
--- a/gcc/config/rs6000/pcrel.md
+++ b/gcc/config/rs6000/pcrel.md
@@ -0,0 +1,598 @@
+;; PC relative support.
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+;; Contributed by Peter Bergner <bergner@linux.ibm.com> and
+;; 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/>.
+
+;;
+;; UNSPEC usage
+;;
+
+(define_c_enum "unspec"
+ [UNSPEC_PCREL_LD
+ UNSPEC_PCREL_ST
+ ])
+
+
+;; Optimize references to external variables to combine loading up the external
+;; address from the GOT and doing the load or store operation.
+;;
+;; A typical optimization looks like:
+;;
+;; pld b,var@pcrel@got(0),1
+;; .Lpcrel1:
+;; ...
+;; .reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8)
+;; lwz r,0(b)
+;;
+;; If 'var' is an external variable defined in another module in the main
+;; program, and the code is being linked for the main program, then the
+;; linker can optimize this to:
+;;
+;; plwz r,var@pcrel
+;; .Lpcrel1:
+;; ...
+;; nop
+;;
+;; If either the variable or the code being linked is defined in a shared
+;; library, then the linker puts the address in the GOT area, and the pld will
+;; load up the pointer, and then that pointer is used for the load or store.
+;; If there is more than one reference to the GOT pointer, the compiler will
+;; not do this optimization, and use the GOT pointer normally.
+;;
+;; Having the label after the pld instruction and using label-8 in the .reloc
+;; addresses the prefixed instruction properly. If we put the label before the
+;; pld instruction, then the relocation might point to the NOP that is
+;; generated if the prefixed instruction is not aligned.
+;;
+;; We need to rewrite the normal GOT load operation before register allocation
+;; to include setting the eventual destination register for loads, or referring
+;; to the value being stored for store operations so that the proper register
+;; lifetime is set in case the optimization is done and the pld/lwz is
+;; converted to plwz/nop.
+;;
+;; If the reference to the external symbol contains an offset, the splitter
+;; code moves the offset to the secondary load or store.
+
+(define_mode_iterator PO [QI HI SI DI SF DF
+ V16QI V8HI V4SI V4SF V2DI V2DF V1TI KF
+ (TF "FLOAT128_IEEE_P (TFmode)")])
+
+;; Vector types for pcrel optimization
+(define_mode_iterator POV [V16QI V8HI V4SI V4SF V2DI V2DF V1TI KF
+ (TF "FLOAT128_IEEE_P (TFmode)")])
+
+;; Define the constraints for each mode for pcrel_opt. The order of the
+;; constraints should have the most natural register class first.
+(define_mode_attr PO_constraint [(QI "r,d,v")
+ (HI "r,d,v")
+ (SI "r,d,v")
+ (DI "r,d,v")
+ (SF "d,v,r")
+ (DF "d,v,r")
+ (V16QI "wa,wn,wn")
+ (V8HI "wa,wn,wn")
+ (V4SI "wa,wn,wn")
+ (V4SF "wa,wn,wn")
+ (V2DI "wa,wn,wn")
+ (V2DF "wa,wn,wn")
+ (V1TI "wa,wn,wn")
+ (KF "wa,wn,wn")
+ (TF "wa,wn,wn")])
+
+;; Combiner pattern that combines the load of the GOT along with the load. The
+;; first split pass before register allocation will split this into the load of
+;; the GOT that indicates the resultant value may be created if the PCREL_OPT
+;; relocation is done.
+;;
+;; The (set (match_dup 0)
+;; (unspec:<MODE> [(const_int 0)] UNSPEC_PCREL_LD))
+;;
+;; Is to signal to the register allocator that the destination register may be
+;; set by the GOT operation (if the linker does the optimization).
+;;
+;; We need to set the "cost" explicitly so that the instruction length is not
+;; used. We return the same cost as a normal load (4 if we are not optimizing
+;; for speed, 8 if we are optimizing for speed)
+
+(define_insn_and_split "*mov<mode>_pcrel_opt_load"
+ [(set (match_operand:PO 0 "gpc_reg_operand")
+ (match_operand:PO 1 "pcrel_external_memory"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64
+ && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 2)
+ (match_dup 3))
+ (set (match_dup 0)
+ (unspec:<MODE> [(const_int 0)] UNSPEC_PCREL_LD))
+ (use (const_int 0))])
+ (parallel [(set (match_dup 0)
+ (match_dup 4))
+ (use (match_dup 0))
+ (use (const_int 0))])]
+{
+ rtx mem = operands[1];
+ rtx addr = XEXP (mem, 0);
+ rtx got = gen_reg_rtx (DImode);
+ rtx gotoff = got;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ gotoff = gen_rtx_PLUS (DImode, got, XEXP (addr, 1));
+ addr = XEXP (addr, 0);
+ }
+
+ operands[2] = got;
+ operands[3] = addr;
+ operands[4] = replace_equiv_address_nv (mem, gotoff, false);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "16")
+ (set (attr "cost")
+ (if_then_else (match_test "optimize_function_for_speed_p (cfun)")
+ (const_string "8")
+ (const_string "4")))
+ (set_attr "prefixed" "yes")])
+
+;; Zero extend combiner patterns
+(define_insn_and_split "*mov<mode>_pcrel_opt_zero_extend"
+ [(set (match_operand:DI 0 "gpc_reg_operand")
+ (zero_extend:DI
+ (match_operand:QHSI 1 "pcrel_external_memory")))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64
+ && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 2)
+ (match_dup 3))
+ (set (match_dup 0)
+ (unspec:DI [(const_int 0)] UNSPEC_PCREL_LD))
+ (use (const_int 0))])
+ (parallel [(set (match_dup 0)
+ (zero_extend:DI
+ (match_dup 4)))
+ (use (match_dup 0))
+ (use (const_int 0))])]
+{
+ rtx mem = operands[1];
+ rtx addr = XEXP (mem, 0);
+ rtx got = gen_reg_rtx (DImode);
+ rtx gotoff = got;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ gotoff = gen_rtx_PLUS (DImode, got, XEXP (addr, 1));
+ addr = XEXP (addr, 0);
+ }
+
+ operands[2] = got;
+ operands[3] = addr;
+ operands[4] = replace_equiv_address_nv (mem, gotoff, false);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "16")
+ (set (attr "cost")
+ (if_then_else (match_test "optimize_function_for_speed_p (cfun)")
+ (const_string "8")
+ (const_string "4")))
+ (set_attr "prefixed" "yes")])
+
+;; Sign extend combiner patterns
+(define_insn_and_split "*mov<mode>_pcrel_opt_sign_extend"
+ [(set (match_operand:DI 0 "gpc_reg_operand")
+ (sign_extend:DI
+ (match_operand:HSI 1 "pcrel_external_memory")))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64
+ && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 2)
+ (match_dup 3))
+ (set (match_dup 0)
+ (unspec:DI [(const_int 0)] UNSPEC_PCREL_LD))
+ (use (const_int 0))])
+ (parallel [(set (match_dup 0)
+ (sign_extend:DI
+ (match_dup 4)))
+ (use (match_dup 0))
+ (use (const_int 0))])]
+{
+ rtx mem = operands[1];
+ rtx addr = XEXP (mem, 0);
+ rtx got = gen_reg_rtx (DImode);
+ rtx gotoff = got;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ gotoff = gen_rtx_PLUS (DImode, got, XEXP (addr, 1));
+ addr = XEXP (addr, 0);
+ }
+
+ operands[2] = got;
+ operands[3] = addr;
+ operands[4] = replace_equiv_address_nv (mem, gotoff, false);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "16")
+ (set (attr "cost")
+ (if_then_else (match_test "optimize_function_for_speed_p (cfun)")
+ (const_string "8")
+ (const_string "4")))
+ (set_attr "prefixed" "yes")])
+
+;; Float extend combiner pattern
+(define_insn_and_split "*movdf_pcrel_opt_float_extend"
+ [(set (match_operand:DF 0 "gpc_reg_operand")
+ (float_extend:DF
+ (match_operand:SF 1 "pcrel_external_memory")))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64
+ && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 2)
+ (match_dup 3))
+ (set (match_dup 0)
+ (unspec:DF [(const_int 0)] UNSPEC_PCREL_LD))
+ (use (const_int 0))])
+ (parallel [(set (match_dup 0)
+ (float_extend:DF
+ (match_dup 4)))
+ (use (match_dup 0))
+ (use (const_int 0))])]
+{
+ rtx mem = operands[1];
+ rtx addr = XEXP (mem, 0);
+ rtx got = gen_reg_rtx (DImode);
+ rtx gotoff = got;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ gotoff = gen_rtx_PLUS (DImode, got, XEXP (addr, 1));
+ addr = XEXP (addr, 0);
+ }
+
+ operands[2] = got;
+ operands[3] = addr;
+ operands[4] = replace_equiv_address_nv (mem, gotoff, false);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "16")
+ (set (attr "cost")
+ (if_then_else (match_test "optimize_function_for_speed_p (cfun)")
+ (const_string "8")
+ (const_string "4")))
+ (set_attr "prefixed" "yes")])
+
+;; Patterns to load up the GOT address that may be changed into the load of the
+;; actual variable.
+(define_insn "*mov<mode>_pcrel_opt_load_got"
+ [(set (match_operand:DI 0 "base_reg_operand" "=b,b,b")
+ (match_operand:DI 1 "pcrel_external_address"))
+ (set (match_operand:PO 2 "gpc_reg_operand" "=<PO_constraint>")
+ (unspec:PO [(const_int 0)] UNSPEC_PCREL_LD))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+{
+ return (INTVAL (operands[3])) ? "ld %0,%a1\n.Lpcrel%3:" : "ld %0,%a1";
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "12")
+ (set_attr "pcrel_opt" "load_got")
+ (set (attr "cost")
+ (if_then_else (match_test "optimize_function_for_speed_p (cfun)")
+ (const_string "8")
+ (const_string "4")))
+ (set_attr "prefixed" "yes")])
+
+;; The secondary load insns that uses the GOT pointer that may become a NOP.
+(define_insn "*mov<mode>_pcrel_opt_load_mem"
+ [(set (match_operand:QHI 0 "gpc_reg_operand" "+r,wa")
+ (match_operand:QHI 1 "memory_operand" "m,Z"))
+ (use (match_operand:QHI 2 "gpc_reg_operand" "0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ l<wd>z%U1%X1 %0,%1
+ lxsi<wd>zx %x0,%y1"
+ [(set_attr "type" "load,fpload")
+ (set_attr "pcrel_opt" "load,no")])
+
+(define_insn "*movsi_pcrel_opt_load_mem"
+ [(set (match_operand:SI 0 "gpc_reg_operand" "+r,d,v")
+ (match_operand:SI 1 "memory_operand" "m,Z,Z"))
+ (use (match_operand:SI 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lwz%U1%X1 %0,%1
+ lfiwzx %0,%y1
+ lxsiwzx %x0,%y1"
+ [(set_attr "type" "load,fpload,fpload")
+ (set_attr "pcrel_opt" "load,no,no")])
+
+(define_insn "*movdi_pcrel_opt_load_mem"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "+r,d,v")
+ (match_operand:DI 1 "memory_operand" "m,m,m"))
+ (use (match_operand:DI 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ ld%U1%X1 %0,%1
+ lfd%U1%X1 %0,%1
+ lxsd%X1 %0,%1"
+ [(set_attr "type" "load,fpload,fpload")
+ (set_attr "pcrel_opt" "load")])
+
+(define_insn "*movsf_pcrel_opt_load_mem"
+ [(set (match_operand:SF 0 "gpc_reg_operand" "+d,v,r")
+ (match_operand:SF 1 "memory_operand" "m,m,m"))
+ (use (match_operand:SF 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lfs%U1%X1 %0,%1
+ lxssp%X1 %0,%1
+ lwz%U1%X1 %0,%1"
+ [(set_attr "type" "fpload,fpload,load")
+ (set_attr "pcrel_opt" "load")])
+
+(define_insn "*movdf_pcrel_opt_load_mem"
+ [(set (match_operand:DF 0 "gpc_reg_operand" "+d,v,r")
+ (match_operand:DF 1 "memory_operand" "m,m,m"))
+ (use (match_operand:DF 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lfd%U1%X1 %0,%1
+ lxsd%X1 %0,%1
+ ld%U1%X1 %0,%1"
+ [(set_attr "type" "fpload,fpload,load")
+ (set_attr "pcrel_opt" "load")])
+
+(define_insn "*mov<mode>_pcrel_opt_load_mem"
+ [(set (match_operand:POV 0 "gpc_reg_operand" "+wa")
+ (match_operand:POV 1 "memory_operand" "m"))
+ (use (match_operand:POV 2 "gpc_reg_operand" "0"))
+ (use (match_operand:DI 3 "const_int_operand" "n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "lxv%X1 %x0,%1"
+ [(set_attr "type" "vecload")
+ (set_attr "pcrel_opt" "load")])
+
+;; Zero extend insns
+(define_insn "*mov<mode>_pcrel_opt_load_zero_extend2"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "+r,wa")
+ (zero_extend:DI
+ (match_operand:QHI 1 "memory_operand" "m,Z")))
+ (use (match_operand:DI 2 "gpc_reg_operand" "0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ l<wd>z%U1%X1 %0,%1
+ lxsi<wd>zx %x0,%y1"
+ [(set_attr "type" "load,fpload")
+ (set_attr "pcrel_opt" "load,no")])
+
+(define_insn "*movsi_pcrel_opt_load_zero_extend2"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "+r,d,v")
+ (zero_extend:DI
+ (match_operand:SI 1 "memory_operand" "m,Z,Z")))
+ (use (match_operand:DI 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lwz%U1%X1 %0,%1
+ lfiwzx %0,%y1
+ lxsiwzx %x0,%y1"
+ [(set_attr "type" "load,fpload,fpload")
+ (set_attr "pcrel_opt" "load,no,no")])
+
+;; Sign extend insns
+(define_insn "*movsi_pcrel_opt_load_sign_extend2"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "+r,d,v")
+ (sign_extend:DI
+ (match_operand:SI 1 "memory_operand" "m,Z,Z")))
+ (use (match_operand:DI 2 "gpc_reg_operand" "0,0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lwa%U1%X1 %0,%1
+ lfiwax %0,%y1
+ lxsiwax %x0,%y1"
+ [(set_attr "type" "load,fpload,fpload")
+ (set_attr "pcrel_opt" "load,no,no")])
+
+(define_insn_and_split "*movhi_pcrel_opt_load_sign_extend2"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "+r,v")
+ (sign_extend:DI
+ (match_operand:HI 1 "memory_operand" "m,Z")))
+ (use (match_operand:DI 2 "gpc_reg_operand" "0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lha%U1%X1 %0,%1
+ #"
+ "&& reload_completed && altivec_register_operand (operands[0], HImode)"
+ [(parallel [(set (match_dup 4)
+ (match_dup 1))
+ (use (match_dup 4))
+ (use (const_int 0))])
+ (set (match_dup 0)
+ (sign_extend:DI
+ (match_dup 4)))]
+{
+ operands[4] = gen_rtx_REG (HImode, REGNO (operands[0]));
+}
+ [(set_attr "type" "load,fpload")
+ (set_attr "length" "4,8")
+ (set_attr "pcrel_opt" "load,no")])
+
+;; Floating point extend insn
+(define_insn "*movsf_pcrel_opt_load_float_extend2"
+ [(set (match_operand:DF 0 "gpc_reg_operand" "+d,v")
+ (float_extend:DF
+ (match_operand:SF 1 "memory_operand" "m,m")))
+ (use (match_operand:DF 2 "gpc_reg_operand" "0,0"))
+ (use (match_operand:DI 3 "const_int_operand" "n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ lfs%U1%X1 %0,%1
+ lxssp%X1 %0,%1"
+ [(set_attr "type" "fpload")
+ (set_attr "pcrel_opt" "load")])
+
+; ;; Store combiner insns that merge together loading up the address of the
+; ;; external variable and doing the store. This is split in the first split
+; ;; pass before register allocation.
+;;
+;; We need to set the "cost" explicitly so that the instruction length is not
+;; used. We return the same cost as a normal store (4).
+(define_insn_and_split "*mov<mode>_pcrel_opt_store"
+ [(set (match_operand:PO 0 "pcrel_external_memory")
+ (match_operand:PO 1 "gpc_reg_operand"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64
+ && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (match_dup 2)
+ (unspec:DI [(match_dup 1)
+ (match_dup 3)
+ (const_int 0)] UNSPEC_PCREL_ST))
+ (parallel [(set (match_dup 4)
+ (match_dup 1))
+ (use (const_int 0))])]
+{
+ rtx mem = operands[0];
+ rtx addr = XEXP (mem, 0);
+ rtx got = gen_reg_rtx (DImode);
+ rtx gotoff = got;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ gotoff = gen_rtx_PLUS (DImode, got, XEXP (addr, 1));
+ addr = XEXP (addr, 0);
+ }
+
+ operands[2] = got;
+ operands[3] = addr;
+ operands[4] = replace_equiv_address_nv (mem, gotoff, false);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "20")
+ (set_attr "pcrel_opt" "store_got")
+ (set_attr "cost" "4")
+ (set_attr "prefixed" "yes")])
+
+;; Load of the GOT address for a store operation that may be converted into a
+;; direct store.
+(define_insn "*mov<mode>_pcrel_opt_store_got"
+ [(set (match_operand:DI 0 "base_reg_operand" "=&b,&b,&b")
+ (unspec:DI [(match_operand:PO 1 "gpc_reg_operand" "<PO_constraint>")
+ (match_operand:DI 2 "pcrel_external_address")
+ (match_operand:DI 3 "const_int_operand" "n,n,n")]
+ UNSPEC_PCREL_ST))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+{
+ return (INTVAL (operands[3])) ? "ld %0,%a2\n.Lpcrel%3:" : "ld %0,%a2";
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "12")
+ (set_attr "pcrel_opt" "store_got")
+ (set_attr "cost" "4")
+ (set_attr "prefixed" "yes")])
+
+;; Secondary store instruction that uses the GOT pointer, and may be optimized
+;; into a NOP instruction.
+(define_insn "*mov<mode>_pcrel_opt_store_mem"
+ [(set (match_operand:QHI 0 "memory_operand" "=m,Z")
+ (match_operand:QHI 1 "gpc_reg_operand" "r,wa"))
+ (use (match_operand:DI 2 "const_int_operand" "n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ st<wd>%U0%X0 %1,%0
+ stxsi<wd>x %x1,%y0"
+ [(set_attr "type" "store,fpstore")
+ (set_attr "pcrel_opt" "store,no")])
+
+(define_insn "*movsi_pcrel_opt_store_mem"
+ [(set (match_operand:SI 0 "memory_operand" "=m,Z,Z")
+ (match_operand:SI 1 "gpc_reg_operand" "r,d,v"))
+ (use (match_operand:DI 2 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ stw%U0%X0 %1,%0
+ stfiwx %1,%y0
+ stxsiwx %1,%y0"
+ [(set_attr "type" "store,fpstore,fpstore")
+ (set_attr "pcrel_opt" "store,no,no")])
+
+(define_insn "*movdi_pcrel_opt_store_mem"
+ [(set (match_operand:DI 0 "memory_operand" "=m,m,m")
+ (match_operand:DI 1 "gpc_reg_operand" "r,d,v"))
+ (use (match_operand:DI 2 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ std%U0%X0 %1,%0
+ stfd%U0%X0 %1,%0
+ stxsd%X0 %1,%0"
+ [(set_attr "type" "store,fpstore,fpstore")
+ (set_attr "pcrel_opt" "store")])
+
+(define_insn "*movsf_pcrel_opt_store_mem"
+ [(set (match_operand:SF 0 "memory_operand" "=m,m,m")
+ (match_operand:SF 1 "gpc_reg_operand" "d,v,r"))
+ (use (match_operand:DI 2 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ stfs%U0%X0 %1,%0
+ stxssp%X0 %1,%0
+ stw%U0%X0 %1,%0"
+ [(set_attr "type" "fpstore,fpstore,store")
+ (set_attr "pcrel_opt" "store")])
+
+(define_insn "*movdf_pcrel_opt_store_mem"
+ [(set (match_operand:DF 0 "memory_operand" "=m,m,m")
+ (match_operand:DF 1 "gpc_reg_operand" "d,v,r"))
+ (use (match_operand:DI 2 "const_int_operand" "n,n,n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "@
+ stfd%U0%X0 %1,%0
+ stxsd%X0 %1,%0
+ std%U0%X0 %1,%0"
+ [(set_attr "type" "fpstore,fpstore,store")
+ (set_attr "pcrel_opt" "store")])
+
+(define_insn "*mov<mode>_pcrel_opt_store_mem"
+ [(set (match_operand:POV 0 "memory_operand" "=m")
+ (match_operand:POV 1 "gpc_reg_operand" "wa"))
+ (use (match_operand:DI 2 "const_int_operand" "n"))]
+ "TARGET_PCREL && TARGET_PCREL_OPT && TARGET_POWERPC64"
+ "stxv%X0 %x1,%0"
+ [(set_attr "type" "vecstore")
+ (set_attr "pcrel_opt" "store")])
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index dbbed50e64b..9c1a37d35f3 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -775,6 +775,13 @@
return indexed_or_indirect_address (op, mode);
})
+;; Return 1 if the operand uses a single register for the address.
+(define_predicate "one_reg_memory_operand"
+ (match_code "mem")
+{
+ return REG_P (XEXP (op, 0));
+})
+
;; Like indexed_or_indirect_operand, but also allow a GPR register if direct
;; moves are supported.
(define_predicate "reg_or_indexed_operand"
@@ -986,9 +993,9 @@
if (CONST_INT_P (op))
return 1;
if (XINT (op, 1) == UNSPEC_TLSGD)
- return REG_P (XVECEXP (op, 0, 1));
+ return REG_P (XVECEXP (op, 0, 1)) || XVECEXP (op, 0, 1) == const0_rtx;
if (XINT (op, 1) == UNSPEC_TLSLD)
- return REG_P (XVECEXP (op, 0, 0));
+ return REG_P (XVECEXP (op, 0, 0)) || XVECEXP (op, 0, 0) == const0_rtx;
return 0;
})
@@ -1810,6 +1817,15 @@
return iform == INSN_FORM_PCREL_EXTERNAL;
})
+;; Return 1 if op is a memory operand to an external variable when we
+;; support pc-relative addressing and the PCREL_OPT relocation to
+;; optimize references to it.
+(define_predicate "pcrel_external_memory"
+ (match_code "mem")
+{
+ return pcrel_external_address (XEXP (op, 0), Pmode);
+})
+
;; Return true if the address is PC-relative and the symbol is either local or
;; external.
(define_predicate "pcrel_local_or_external_address"
diff --git a/gcc/config/rs6000/rs6000-cpus.def b/gcc/config/rs6000/rs6000-cpus.def
index 815da92d1ba..e4f8ffa82bc 100644
--- a/gcc/config/rs6000/rs6000-cpus.def
+++ b/gcc/config/rs6000/rs6000-cpus.def
@@ -87,6 +87,7 @@
prefixed addressing, and we want to clear all of the addressing bits
on targets that cannot support prefixed/pcrel addressing. */
#define ADDRESSING_FUTURE_MASKS (OPTION_MASK_PCREL \
+ | OPTION_MASK_PCREL_OPT \
| OPTION_MASK_PREFIXED_ADDR)
/* Flags that need to be turned off if -mno-future. */
@@ -146,6 +147,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 \
diff --git a/gcc/config/rs6000/rs6000-passes.def b/gcc/config/rs6000/rs6000-passes.def
index ad30a45bc13..28f9d088646 100644
--- a/gcc/config/rs6000/rs6000-passes.def
+++ b/gcc/config/rs6000/rs6000-passes.def
@@ -25,3 +25,12 @@ along with GCC; see the file COPYING3. If not see
*/
INSERT_PASS_BEFORE (pass_cse, 1, pass_analyze_swaps);
+
+/* The pcrel_opt pass must be the final pass before final. This pass combines
+ references to external pc-relative variables with their use. There must be
+ only one reference to the external pointer loaded in order to do the
+ optimization. Otherwise we load up the addresses (either via PADDI if the
+ label is local or via a PLD from the got section if it is defined in another
+ module) and the value as a base pointer. */
+
+ INSERT_PASS_BEFORE (pass_final, 1, pass_pcrel_opt);
diff --git a/gcc/config/rs6000/rs6000-pcrel.c b/gcc/config/rs6000/rs6000-pcrel.c
index e69de29bb2d..ab41dc0ff56 100644
--- a/gcc/config/rs6000/rs6000-pcrel.c
+++ b/gcc/config/rs6000/rs6000-pcrel.c
@@ -0,0 +1,497 @@
+/* Subroutines used support the pc-relative linker optimization.
+ Copyright (C) 2019 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/store that uses that GOT pointer. If that is found we create the
+ PCREL_OPT relocation to possibly convert:
+
+ pld b,var@pcrel@got(0),1
+
+ # possibly other instructions that do not use the base register 'b' or
+ # the result register 'r'.
+
+ lwz r,0(b)
+
+ into:
+
+ plwz r,var@pcrel(0),1
+
+ # possibly other instructions that do not use the base register 'b' or
+ # the result register 'r'.
+
+ nop
+
+ If the variable is not defined in the main program or the code using it is
+ not in the main program, the linker put the address in the .got section and
+ do:
+
+ .section .got
+ .Lvar_got: .dword var
+
+ .section .text
+ pld b,.Lvar_got@pcrel(0),1
+
+ # possibly other instructions that do not use the base register 'b' or
+ # the result register 'r'.
+
+ lwz r,0(b)
+
+ We only look for a single usage in the basic block where the GOT pointer 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 "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"
+
+
+// Optimize pc-relative references
+const pass_data pass_data_pcrel =
+{
+ RTL_PASS, // type
+ "pcrel", // 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 : public rtl_opt_pass
+{
+private:
+ // Function to optimize pc relative loads/stores
+ unsigned int do_pcrel_opt (function *);
+
+ // A GOT pointer used for a load
+ void load_got (rtx_insn *);
+
+ // A load insn that uses the GOT ponter
+ void load_insn (rtx_insn *);
+
+ // A GOT pointer used for a store
+ void store_got (rtx_insn *);
+
+ // A store insn that uses the GOT ponter
+ void store_insn (rtx_insn *);
+
+ // Record the number of loads and stores optimized
+ unsigned long num_got_loads;
+ unsigned long num_got_stores;
+ unsigned long num_loads;
+ unsigned long num_stores;
+ unsigned long num_opt_loads;
+ unsigned long num_opt_stores;
+
+ // We record the GOT insn for each register that sets a GOT for a load or a
+ // store instruction.
+ rtx_insn *got_reg[32];
+
+public:
+ pcrel (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_pcrel, ctxt),
+ num_got_loads (0),
+ num_got_stores (0),
+ num_loads (0),
+ num_stores (0),
+ num_opt_loads (0),
+ num_opt_stores (0)
+ {}
+
+ ~pcrel (void)
+ {}
+
+ // opt_pass methods:
+ virtual bool gate (function *)
+ {
+ return TARGET_PCREL && TARGET_PCREL_OPT && optimize;
+ }
+
+ virtual unsigned int execute (function *fun)
+ {
+ return do_pcrel_opt (fun);
+ }
+
+ opt_pass *clone ()
+ {
+ return new pcrel (m_ctxt);
+ }
+};
+
+
+/* Return a marker to create the backward pointing label that links the load or
+ store to the insn that loads the adddress of an external label with
+ PCREL_GOT. This allows us to create the necessary R_PPC64_PCREL_OPT
+ relocation to link the two instructions. */
+
+static rtx
+pcrel_marker (void)
+{
+ static unsigned int label_number = 0;
+
+ label_number++;
+ return GEN_INT (label_number);
+}
+
+
+// Save the current PCREL_OPT load GOT insn address in the register # of the
+// GOT pointer that is loaded.
+//
+// The PCREL_OPT LOAD_GOT insn looks like:
+//
+// (parallel [(set (base) (addr))
+// (set (reg) (unspec [(const_int 0)] UNSPEC_PCREL_LD))
+// (use (marker))])
+//
+// The base register is the GOT address, and the marker is a numeric label that
+// is created in this pass if the only use of the GOT load pointer is for a
+// single load.
+
+void
+pcrel::load_got (rtx_insn *insn)
+{
+ rtx pattern = PATTERN (insn);
+ rtx set = XVECEXP (pattern, 0, 0);
+ int got = REGNO (SET_DEST (set));
+
+ gcc_assert (IN_RANGE (got, FIRST_GPR_REGNO+1, LAST_GPR_REGNO));
+ got_reg[got] = insn;
+ num_got_loads++;
+}
+
+// See if the use of this load of a GOT pointer is the only usage. If so,
+// allocate a marker to create a label.
+//
+// The PCREL_OPT LOAD insn looks like:
+//
+// (parallel [(set (reg) (mem))
+// (use (reg)
+// (use (marker))])
+//
+// Between the reg and the memory might be a SIGN_EXTEND, ZERO_EXTEND, or
+// FLOAT_EXTEND:
+//
+// (parallel [(set (reg) (sign_extend (mem)))
+// (use (reg)
+// (use (marker))])
+
+void
+pcrel::load_insn (rtx_insn *insn)
+{
+ num_loads++;
+
+ /* If the optimizer has changed the load instruction, just use the GOT
+ pointer as an address. */
+ rtx pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != PARALLEL || XVECLEN (pattern, 0) != 3)
+ return;
+
+ /* If the instruction is not an offsettable instruction just use the GOT
+ pointer as an address. */
+ if (get_attr_update (insn) == UPDATE_YES
+ || get_attr_indexed (insn) == INDEXED_YES)
+ return;
+
+ /* The PCREL_OPT ABI does not support having the load instruction being
+ prefixed. Even if it did allow it, we would need to be careful in setting
+ up the relocation, because prefixed instructions might have an optional
+ NOP before the instruction. */
+ if (prefixed_load_p (insn))
+ return;
+
+ rtx set = XVECEXP (pattern, 0, 0);
+ if (GET_CODE (set) != SET
+ || GET_CODE (XVECEXP (pattern, 0, 1)) != USE
+ || GET_CODE (XVECEXP (pattern, 0, 2)) != USE)
+ return;
+
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ if (!rtx_equal_p (dest, XEXP (XVECEXP (pattern, 0, 1), 0)))
+ return;
+
+ if (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND
+ || GET_CODE (src) == FLOAT_EXTEND)
+ src = XEXP (src, 0);
+
+ if (!MEM_P (src))
+ return;
+
+ rtx addr = XEXP (src, 0);
+
+ /* The address might be a numeric offset from a register. We don't care at
+ this point about the offset, just the base register. */
+ if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1)))
+ addr = XEXP (addr, 0);
+
+ if (!REG_P (addr))
+ return;
+
+ int r = REGNO (addr);
+ if (!IN_RANGE (r, FIRST_GPR_REGNO+1, LAST_GPR_REGNO))
+ return;
+
+ rtx_insn *got_insn = got_reg[r];
+
+ // See if this is the only reference, and there is a set of the GOT pointer
+ // previously in the same basic block. If this is the only reference,
+ // optimize it.
+ if (got_insn
+ && get_attr_pcrel_opt (got_insn) == PCREL_OPT_LOAD_GOT
+ && !reg_used_between_p (addr, got_insn, insn)
+ && (find_reg_note (insn, REG_DEAD, addr) || rtx_equal_p (dest, addr)))
+ {
+ rtx marker = pcrel_marker ();
+ rtx got_use = XVECEXP (PATTERN (got_insn), 0, 2);
+ rtx insn_use = XVECEXP (pattern, 0, 2);
+
+ gcc_checking_assert (rtx_equal_p (XEXP (got_use, 0), const0_rtx));
+ gcc_checking_assert (rtx_equal_p (XEXP (insn_use, 0), const0_rtx));
+
+ XEXP (got_use, 0) = marker;
+ XEXP (insn_use, 0) = marker;
+ num_opt_loads++;
+ }
+
+ // Forget the GOT now that we've used it.
+ got_reg[r] = (rtx_insn *)0;
+}
+
+// Save the current PCREL_OPT store GOT insn address in the register # of the
+// GOT pointer that is loaded.
+//
+// The PCREL_OPT STORE_GOT insn looks like:
+//
+// (set (set (base)
+// (unspec:DI [(src)
+// (addr)
+// (marker)] UNSPEC_PCREL_ST))
+//
+// The base register is the GOT address, and the marker is a numeric label that
+// is created in this pass or 0 to indicate there are other uses of the GOT
+// pointer.
+
+void
+pcrel::store_got (rtx_insn *insn)
+{
+ rtx pattern = PATTERN (insn);
+ int got = REGNO (SET_DEST (pattern));
+
+ gcc_checking_assert (IN_RANGE (got, FIRST_GPR_REGNO+1, LAST_GPR_REGNO));
+ got_reg[got] = insn;
+ num_got_stores++;
+}
+
+// See if the use of this store using a GOT pointer is the only usage. If so,
+// allocate a marker to create a label.
+//
+// The PCREL_OPT STORE insn looks like:
+//
+// (parallel [(set (mem) (reg))
+// (use (marker))])
+
+void
+pcrel::store_insn (rtx_insn *insn)
+{
+ num_stores++;
+
+ /* If the optimizer has changed the store instruction, just use the GOT
+ pointer as an address. */
+ rtx pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != PARALLEL || XVECLEN (pattern, 0) != 2)
+ return;
+
+ /* If the instruction is not an offsettable instruction just use the GOT
+ pointer as an address. */
+ if (get_attr_update (insn) == UPDATE_YES
+ || get_attr_indexed (insn) == INDEXED_YES)
+ return;
+
+ /* The PCREL_OPT ABI does not support having the store instruction being
+ prefixed. Even if it did allow it, we would need to be careful in setting
+ up the relocation, because prefixed instructions might have an optional
+ NOP before the instruction. */
+ if (prefixed_store_p (insn))
+ return;
+
+ rtx set = XVECEXP (pattern, 0, 0);
+ if (GET_CODE (set) != SET || GET_CODE (XVECEXP (pattern, 0, 1)) != USE)
+ return;
+
+ rtx dest = SET_DEST (set);
+
+ if (!MEM_P (dest))
+ return;
+
+ rtx addr = XEXP (dest, 0);
+
+ /* The address might be a numeric offset from a register. We don't care at
+ this point about the offset, just the base register. */
+ if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1)))
+ addr = XEXP (addr, 0);
+
+ if (!REG_P (addr))
+ return;
+
+ int r = REGNO (addr);
+ if (!IN_RANGE (r, FIRST_GPR_REGNO+1, LAST_GPR_REGNO))
+ return;
+
+ rtx_insn *got_insn = got_reg[r];
+
+ // See if this is the only reference, and there is a GOT pointer previously.
+ // If this is the only reference, optimize it.
+ if (got_insn
+ && get_attr_pcrel_opt (got_insn) == PCREL_OPT_STORE_GOT
+ && !reg_used_between_p (addr, got_insn, insn)
+ && find_reg_note (insn, REG_DEAD, addr))
+ {
+ rtx marker = pcrel_marker ();
+ rtx got_src = SET_SRC (PATTERN (got_insn));
+ rtx insn_use = XVECEXP (pattern, 0, 1);
+
+ gcc_checking_assert (rtx_equal_p (XVECEXP (got_src, 0, 2), const0_rtx));
+ gcc_checking_assert (rtx_equal_p (XEXP (insn_use, 0), const0_rtx));
+
+ XVECEXP (got_src, 0, 2) = marker;
+ XEXP (insn_use, 0) = marker;
+ num_opt_stores++;
+ }
+
+ // Forget the GOT now
+ got_reg[r] = (rtx_insn *)0;
+}
+
+// Optimize pcrel external variable references
+
+unsigned int
+pcrel::do_pcrel_opt (function *fun)
+{
+ basic_block bb;
+ rtx_insn *insn, *curr_insn = 0;
+
+ // Dataflow analysis for use-def chains.
+ df_set_flags (DF_RD_PRUNE_DEAD_DEFS);
+ df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN);
+ df_analyze ();
+ df_set_flags (DF_DEFER_INSN_RESCAN | DF_LR_RUN_DCE);
+
+ // Look at each basic block to see if there is a load of an external
+ // variable's GOT address, and a single load/store using that GOT address.
+ FOR_ALL_BB_FN (bb, fun)
+ {
+ bool clear_got_p = true;
+
+ FOR_BB_INSNS_SAFE (bb, insn, curr_insn)
+ {
+ if (clear_got_p)
+ {
+ memset ((void *) &got_reg[0], 0, sizeof (got_reg));
+ clear_got_p = false;
+ }
+
+ if (NONJUMP_INSN_P (insn))
+ {
+ rtx pattern = PATTERN (insn);
+ if (GET_CODE (pattern) == SET || GET_CODE (pattern) == PARALLEL)
+ {
+ switch (get_attr_pcrel_opt (insn))
+ {
+ case PCREL_OPT_NO:
+ break;
+
+ case PCREL_OPT_LOAD_GOT:
+ load_got (insn);
+ break;
+
+ case PCREL_OPT_LOAD:
+ load_insn (insn);
+ break;
+
+ case PCREL_OPT_STORE_GOT:
+ store_got (insn);
+ break;
+
+ case PCREL_OPT_STORE:
+ store_insn (insn);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ }
+
+ /* Don't let the GOT load be moved before a label, jump, or call and
+ the dependent load/store after the label, jump, or call. */
+ else if (JUMP_P (insn) || CALL_P (insn) || LABEL_P (insn))
+ clear_got_p = true;
+ }
+ }
+
+ // Rebuild ud chains.
+ df_remove_problem (df_chain);
+ df_process_deferred_rescans ();
+ df_set_flags (DF_RD_PRUNE_DEAD_DEFS | DF_LR_RUN_DCE);
+ df_chain_add_problem (DF_UD_CHAIN);
+ df_analyze ();
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "\npc-relative optimizations:\n");
+ fprintf (dump_file, "\tgot loads = %lu\n", num_got_loads);
+ fprintf (dump_file, "\tpotential loads = %lu\n", num_loads);
+ fprintf (dump_file, "\toptimized loads = %lu\n", num_opt_loads);
+ fprintf (dump_file, "\tgot stores = %lu\n", num_got_stores);
+ fprintf (dump_file, "\tpotential stores = %lu\n", num_stores);
+ fprintf (dump_file, "\toptimized stores = %lu\n\n", num_opt_stores);
+ }
+
+ return 0;
+}
+
+
+rtl_opt_pass *
+make_pass_pcrel_opt (gcc::context *ctxt)
+{
+ return new pcrel (ctxt);
+}
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index 131cd2c047a..81271e52b9a 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -303,6 +303,7 @@ extern bool rs6000_linux_float_exceptions_rounding_supported_p (void);
namespace gcc { class context; }
class rtl_opt_pass;
+extern rtl_opt_pass *make_pass_pcrel_opt (gcc::context *);
extern rtl_opt_pass *make_pass_analyze_swaps (gcc::context *);
extern bool rs6000_sum_of_two_registers_p (const_rtx expr);
extern bool rs6000_quadword_masked_address_p (const_rtx exp);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index d57f7c8c09e..0be0a0f7f09 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -4283,7 +4283,7 @@ rs6000_option_override_internal (bool global_init_p)
if ((rs6000_isa_flags_explicit & OPTION_MASK_PCREL) != 0)
error ("%qs requires %qs", "-mpcrel", "-mcmodel=medium");
- rs6000_isa_flags &= ~OPTION_MASK_PCREL;
+ rs6000_isa_flags &= ~(OPTION_MASK_PCREL | OPTION_MASK_PCREL_OPT);
}
/* Enable defaults if desired. */
@@ -4297,7 +4297,11 @@ rs6000_option_override_internal (bool global_init_p)
if (!explicit_pcrel && TARGET_PCREL_DEFAULT
&& TARGET_CMODEL == CMODEL_MEDIUM)
- rs6000_isa_flags |= OPTION_MASK_PCREL;
+ {
+ rs6000_isa_flags |= OPTION_MASK_PCREL;
+ if ((rs6000_isa_flags_explicit & OPTION_MASK_PCREL_OPT) == 0)
+ rs6000_isa_flags |= OPTION_MASK_PCREL_OPT;
+ }
}
}
@@ -4321,6 +4325,25 @@ rs6000_option_override_internal (bool global_init_p)
rs6000_isa_flags &= ~OPTION_MASK_PCREL;
}
+ /* -mpcrel requires tls marker support. */
+ if (TARGET_PCREL && !TARGET_TLS_MARKERS)
+ {
+ if ((rs6000_isa_flags_explicit & OPTION_MASK_PCREL) != 0)
+ error ("%qs requires %qs", "-mpcrel", "-mtls-markers");
+
+ rs6000_isa_flags &= ~(OPTION_MASK_PCREL
+ | OPTION_MASK_PCREL_OPT);
+ }
+
+ /* Check -mfuture debug switches. */
+ if (!TARGET_PCREL && TARGET_PCREL_OPT)
+ {
+ if ((rs6000_isa_flags_explicit & OPTION_MASK_PCREL_OPT) != 0)
+ error ("%qs requires %qs", "-mpcrel-opt", "-mpcrel");
+
+ 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);
@@ -8622,7 +8645,8 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
return rs6000_legitimize_tls_address_aix (addr, model);
dest = gen_reg_rtx (Pmode);
- if (model == TLS_MODEL_LOCAL_EXEC && rs6000_tls_size == 16)
+ if (model == TLS_MODEL_LOCAL_EXEC
+ && (rs6000_tls_size == 16 || rs6000_pcrel_p (cfun)))
{
rtx tlsreg;
@@ -8669,7 +8693,9 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
them in the .got section. So use a pointer to the .got section,
not one to secondary TOC sections used by 64-bit -mminimal-toc,
or to secondary GOT sections used by 32-bit -fPIC. */
- if (TARGET_64BIT)
+ if (rs6000_pcrel_p (cfun))
+ got = const0_rtx;
+ else if (TARGET_64BIT)
got = gen_rtx_REG (Pmode, 2);
else
{
@@ -8744,7 +8770,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
rtx uns = gen_rtx_UNSPEC (Pmode, vec, UNSPEC_TLS_GET_ADDR);
set_unique_reg_note (get_last_insn (), REG_EQUAL, uns);
- if (rs6000_tls_size == 16)
+ if (rs6000_tls_size == 16 || rs6000_pcrel_p (cfun))
{
if (TARGET_64BIT)
insn = gen_tls_dtprel_64 (dest, tmp1, addr);
@@ -8785,7 +8811,14 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
else
insn = gen_tls_got_tprel_32 (tmp2, got, addr);
emit_insn (insn);
- if (TARGET_64BIT)
+ if (rs6000_pcrel_p (cfun))
+ {
+ if (TARGET_64BIT)
+ insn = gen_tls_tls_pcrel_64 (dest, tmp2, addr);
+ else
+ insn = gen_tls_tls_pcrel_32 (dest, tmp2, addr);
+ }
+ else if (TARGET_64BIT)
insn = gen_tls_tls_64 (dest, tmp2, addr);
else
insn = gen_tls_tls_32 (dest, tmp2, addr);
@@ -23033,6 +23066,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 },
@@ -25288,16 +25322,40 @@ make_memory_non_prefixed (rtx mem)
instruction is printed out. */
static bool next_insn_prefixed_p;
+/* Numeric label that is the address of the GOT load instruction + 8 that we
+ link the R_PPC64_PCREL_OPT relocation to for on the next instruction. */
+static unsigned int pcrel_opt_label_num;
+
/* Define FINAL_PRESCAN_INSN if some processing needs to be done before
outputting the assembler code. On the PowerPC, we remember if the current
insn is a prefixed insn where we need to emit a 'p' before the insn.
- In addition, if the insn is part of a PC-relative reference to an external
+ In addition, if the insn is part of a pc-relative reference to an external
label optimization, this is recorded also. */
void
-rs6000_final_prescan_insn (rtx_insn *insn, rtx [], int)
+rs6000_final_prescan_insn (rtx_insn *insn, rtx operands[], int noperands)
{
next_insn_prefixed_p = (get_attr_prefixed (insn) != PREFIXED_NO);
+
+ enum attr_pcrel_opt pcrel_attr = get_attr_pcrel_opt (insn);
+
+ /* For the load and store instructions that are tied to a GOT pointer, we
+ know that operand 3 contains a marker for loads and operand 2 contains
+ the marker for stores. If it is non-zero, it is the numeric label where
+ we load the address + 8. */
+ if (pcrel_attr == PCREL_OPT_LOAD)
+ {
+ gcc_assert (noperands >= 3);
+ pcrel_opt_label_num = INTVAL (operands[3]);
+ }
+ else if (pcrel_attr == PCREL_OPT_STORE)
+ {
+ gcc_assert (noperands >= 2);
+ pcrel_opt_label_num = INTVAL (operands[2]);
+ }
+ else
+ pcrel_opt_label_num = 0;
+
return;
}
@@ -25307,8 +25365,18 @@ rs6000_final_prescan_insn (rtx_insn *insn, rtx [], int)
void
rs6000_asm_output_opcode (FILE *stream)
{
+ if (pcrel_opt_label_num)
+ {
+ fprintf (stream, ".reloc .Lpcrel%u-8,R_PPC64_PCREL_OPT,.-(.Lpcrel%u-8)\n\t",
+ pcrel_opt_label_num, pcrel_opt_label_num);
+ pcrel_opt_label_num = 0;
+ }
+
if (next_insn_prefixed_p)
- fprintf (stream, "p");
+ {
+ fprintf (stream, "p");
+ next_insn_prefixed_p = false;
+ }
return;
}
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 8a88f14a6f4..d095c9542dc 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -86,6 +86,7 @@
UNSPEC_TLSTPRELLO
UNSPEC_TLSGOTTPREL
UNSPEC_TLSTLS
+ UNSPEC_TLSTLS_PCREL
UNSPEC_FIX_TRUNC_TF ; fadd, rounding towards zero
UNSPEC_STFIWX
UNSPEC_POPCNTB
@@ -257,6 +258,31 @@
(define_attr "cannot_copy" "no,yes" (const_string "no"))
+;; Whether this instruction is part of the two instruction sequence that
+;; supports PCREL_OPT optimizations, where the linker can change code of the
+;; form:
+;;
+;; pld b,var@got@pcrel
+;; 100:
+;; # possibly other instructions
+;; .reloc 100b-8,R_PPC64_PCREL_OPT,0
+;; lwz r,0(b)
+;;
+;; into the following if 'var' is in the main program:
+;;
+;; plwz r,0(b)
+;; # possibly other instructions
+;; nop
+;;
+;; The states are:
+;; no -- insn is not involved with PCREL_OPT optimizations
+;; load_got -- insn loads up the GOT pointer for a load instruction
+;; load -- insn is an offsettable load that uses the GOT pointer
+;; store_got -- insn loads up the GOT pointer for a store instruction
+;; store -- insn is an offsettable store that uses the GOT pointer
+
+(define_attr "pcrel_opt" "no,load_got,load,store_got,store" (const_string "no"))
+
;; Whether an insn is a prefixed insn, and an initial 'p' should be printed
;; before the instruction. A prefixed instruction has a prefix instruction
;; word that extends the immediate value of the instructions from 12-16 bits to
@@ -9497,6 +9523,15 @@
;; TLS support.
+(define_insn "*tls_gd_pcrel<bits>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+ (unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
+ (const_int 0)]
+ UNSPEC_TLSGD))]
+ "HAVE_AS_TLS && TARGET_TLS_MARKERS"
+ "la %0,%1@got@tlsgd@pcrel"
+ [(set_attr "prefixed" "yes")])
+
(define_insn_and_split "*tls_gd<bits>"
[(set (match_operand:P 0 "gpc_reg_operand" "=b")
(unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
@@ -9537,6 +9572,14 @@
"HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
"addi %0,%1,%2@got@tlsgd@l")
+(define_insn "*tls_ld_pcrel<bits>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+ (unspec:P [(const_int 0)]
+ UNSPEC_TLSLD))]
+ "HAVE_AS_TLS && TARGET_TLS_MARKERS"
+ "la %0,%&@got@tlsld@pcrel"
+ [(set_attr "prefixed" "yes")])
+
(define_insn_and_split "*tls_ld<bits>"
[(set (match_operand:P 0 "gpc_reg_operand" "=b")
(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
@@ -9580,7 +9623,11 @@
(match_operand:P 2 "rs6000_tls_symbol_ref" "")]
UNSPEC_TLSDTPREL))]
"HAVE_AS_TLS"
- "addi %0,%1,%2@dtprel")
+ "addi %0,%1,%2@dtprel"
+ [(set (attr "prefixed")
+ (if_then_else (match_test "rs6000_tls_size == 16")
+ (const_string "no")
+ (const_string "yes")))])
(define_insn "tls_dtprel_ha_<bits>"
[(set (match_operand:P 0 "gpc_reg_operand" "=r")
@@ -9644,7 +9691,11 @@
(match_operand:P 2 "rs6000_tls_symbol_ref" "")]
UNSPEC_TLSTPREL))]
"HAVE_AS_TLS"
- "addi %0,%1,%2@tprel")
+ "addi %0,%1,%2@tprel"
+ [(set (attr "prefixed")
+ (if_then_else (match_test "rs6000_tls_size == 16")
+ (const_string "no")
+ (const_string "yes")))])
(define_insn "tls_tprel_ha_<bits>"
[(set (match_operand:P 0 "gpc_reg_operand" "=r")
@@ -9662,6 +9713,15 @@
"HAVE_AS_TLS"
"addi %0,%1,%2@tprel@l")
+(define_insn "*tls_got_tprel_pcrel_<bits>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+ (unspec:P [(const_int 0)
+ (match_operand:P 1 "rs6000_tls_symbol_ref" "")]
+ UNSPEC_TLSGOTTPREL))]
+ "HAVE_AS_TLS"
+ "<ptrload> %0,%1@got@tprel@pcrel"
+ [(set_attr "prefixed" "yes")])
+
;; "b" output constraint here and on tls_tls input to support linker tls
;; optimization. The linker may edit the instructions emitted by a
;; tls_got_tprel/tls_tls pair to addis,addi.
@@ -9705,6 +9765,14 @@
"HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
"<ptrload> %0,%2@got@tprel@l(%1)")
+(define_insn "tls_tls_pcrel_<bits>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
+ (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+ UNSPEC_TLSTLS_PCREL))]
+ "TARGET_ELF && HAVE_AS_TLS"
+ "add %0,%1,%2@tls@pcrel")
+
(define_insn "tls_tls_<bits>"
[(set (match_operand:P 0 "gpc_reg_operand" "=r")
(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
@@ -14742,6 +14810,7 @@
[(set_attr "type" "logical")])
+(include "pcrel.md")
(include "sync.md")
(include "vector.md")
(include "vsx.md")
diff --git a/gcc/config/rs6000/rs6000.opt b/gcc/config/rs6000/rs6000.opt
index 2e0155c41ac..9b45730ef95 100644
--- a/gcc/config/rs6000/rs6000.opt
+++ b/gcc/config/rs6000/rs6000.opt
@@ -578,6 +578,10 @@ mpcrel
Target Report 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.
+
mvector-256bit
Target Report Mask(VECTOR_256BIT) Var(rs6000_isa_flags)
Generate (do not generate) paired vector loads and stores.
diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000
index c2da3032872..ed5efa6f377 100644
--- a/gcc/config/rs6000/t-rs6000
+++ b/gcc/config/rs6000/t-rs6000
@@ -47,6 +47,10 @@ rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call.c
$(COMPILE) $<
$(POSTCOMPILE)
+rs6000-pcrel.o: $(srcdir)/config/rs6000/rs6000-pcrel.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+
$(srcdir)/config/rs6000/rs6000-tables.opt: $(srcdir)/config/rs6000/genopt.sh \
$(srcdir)/config/rs6000/rs6000-cpus.def
$(SHELL) $(srcdir)/config/rs6000/genopt.sh $(srcdir)/config/rs6000 > \
@@ -79,6 +83,7 @@ MD_INCLUDES = $(srcdir)/config/rs6000/rs64.md \
$(srcdir)/config/rs6000/predicates.md \
$(srcdir)/config/rs6000/constraints.md \
$(srcdir)/config/rs6000/darwin.md \
+ $(srcdir)/config/rs6000/pcrel.md \
$(srcdir)/config/rs6000/sync.md \
$(srcdir)/config/rs6000/vector.md \
$(srcdir)/config/rs6000/vsx.md \
diff --git a/gcc/testsuite/ChangeLog.meissner b/gcc/testsuite/ChangeLog.meissner
index 6edb860e581..c97c76bc909 100644
--- a/gcc/testsuite/ChangeLog.meissner
+++ b/gcc/testsuite/ChangeLog.meissner
@@ -1,5 +1,11 @@
2019-10-08 Michael Meissner <meissner@linux.ibm.com>
+ * gcc.target/powerpc/pcrel-opt-di.c: New tests for PCREL_OPT.
+ * gcc.target/powerpc/pcrel-opt-di2.c: Likewise.
+ * gcc.target/powerpc/pcrel-opt-di3.c: Likewise.
+
+2019-10-08 Michael Meissner <meissner@linux.ibm.com>
+
* gcc/testsuite/gcc.target/powerpc/prefix-pcrel.h: New set of
tests to test prefixed addressing on 'future' system with
PC-relative tests.
diff --git a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di.c b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di.c
index e69de29bb2d..b2c3e79e151 100644
--- a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di.c
+++ b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* { dg-require-effective-target powerpc_future_ok } */
+/* { dg-options "-mdejagnu-cpu=future -O2" } */
+
+/* Determine if the pc-relative optimization using the R_PPC64_PCREL_OPT
+ optimization is supported. */
+
+#ifndef TYPE
+#define TYPE long
+#endif
+
+extern TYPE ext;
+
+/* This should generate:
+ PLD 9,ext@got@pcrel
+ .Label:
+ .reloc .Label-8,R_PPC64_PCREL_OPT,0
+ LD 3,0(9) */
+TYPE
+get_ext (void)
+{
+ return ext;
+}
+
+/* This should generate:
+ PLD 9,ext@got@pcrel
+ .Label:
+ .reloc .Label-8,R_PPC64_PCREL_OPT,0
+ STD 3,0(9) */
+
+void
+set_ext (TYPE a)
+{
+ ext = a;
+}
+
+/* Because it has two references to 'ext', this should not generate a
+ R_PPC64_PCREL_OPT relocation. Instead it should generate:
+ PLD 10,ext@got@pcrel
+ LD 9,0(10)
+ ADDI 9,9,1
+ STD 9,0(10) */
+
+void
+inc_ext (void)
+{
+ ext++;
+}
+
+/* { dg-final { scan-assembler-times "ext@got@pcrel" 3 } } */
+/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
+
diff --git a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di2.c b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di2.c
index e69de29bb2d..19204e2a332 100644
--- a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di2.c
+++ b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di2.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* { dg-require-effective-target powerpc_future_ok } */
+/* { dg-options "-mdejagnu-cpu=future -O2" } */
+
+/* Determine if the pc-relative optimization using the R_PPC64_PCREL_OPT
+ optimization is supported. */
+
+#ifndef TYPE
+#define TYPE long
+#endif
+
+#ifndef OFFSET
+#define OFFSET 10
+#endif
+
+extern TYPE ext[];
+
+/* This should generate:
+ PLD 9,ext@got@pcrel
+ .Label:
+ .reloc .Label-8,R_PPC64_PCREL_OPT,0
+ LD 3,80(9) */
+TYPE
+get_ext (void)
+{
+ return ext[OFFSET];
+}
+
+/* This should generate:
+ PLD 9,ext@got@pcrel
+ .Label:
+ .reloc .Label-8,R_PPC64_PCREL_OPT,0
+ STD 3,80(9) */
+
+void
+set_ext (TYPE a)
+{
+ ext[OFFSET] = a;
+}
+
+/* Because it has two references to 'ext', this should not generate a
+ R_PPC64_PCREL_OPT relocation. Instead it should generate:
+ PLD 10,ext@got@pcrel
+ LD 9,80(10)
+ ADDI 9,9,1
+ STD 9,80(10) */
+
+void
+inc_ext (void)
+{
+ (ext[OFFSET])++;
+}
+
+/* { dg-final { scan-assembler-times "ext@got@pcrel" 3 } } */
+/* { dg-final { scan-assembler-times "R_PPC64_PCREL_OPT" 2 } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di3.c b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di3.c
index e69de29bb2d..29148fa74e4 100644
--- a/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di3.c
+++ b/gcc/testsuite/gcc.target/powerpc/pcrel-opt-di3.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_future_ok } */
+/* { dg-options "-mdejagnu-cpu=future -O2" } */
+
+/* Verify that we do not generate PCREL_OPT relocations if the resulting
+ address is prefixed. */
+
+#ifndef TYPE
+#define TYPE long
+#endif
+
+#ifndef OFFSET
+#define OFFSET 0x100000
+#endif
+
+extern TYPE ext[];
+
+/* This should generate:
+ PLD 9,ext@got@pcrel
+ PLD 3,8388608(9)
+
+ and not:
+
+ PLD 9,ext@got@pcrel
+ .Label:
+ .reloc .Label-8,R_PPC64_PCREL_OPT,0
+ PLD 3,8388608(9) */
+
+TYPE
+get_ext (void)
+{
+ return ext[OFFSET];
+}
+
+/* { dg-final { scan-assembler "ext@got@pcrel" } } */
+/* { dg-final { scan-assembler-not "R_PPC64_PCREL_OPT" } } */