summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2015-08-28 19:14:49 -0700
committerH.J. Lu <hjl.tools@gmail.com>2015-10-27 07:56:45 -0700
commitc376fd4175a34c3212ad18a21604a89970938d14 (patch)
tree84ad2663170aacb58297355d7c1faab806e36a36
parent543d5cb877862a149069011befd5b07fb0d253e7 (diff)
downloadgcc-hjl/pic/master.tar.gz
Load specific external function addresses via GOT slothjl/pic/master
Drawbacks with -fno-plt and noplt attribute are 1. -fno-plt may force locally defined functions to be called via their GOT slots through indirect branch, instead of direct branch. 2. noplt attribute doesn't work on libcalls of builtin functions. 3. noplt attribute requires modifying source codes which may not be desirable for third party source packages. Add -fno-plt=file and -fno-plt=[symbol,...] options to specify which external function addresses should be loaded from the GOT slot to avoid PLT. We don't set REG_EQUAL note with external function symbols whose address are loaded from GOT slots so that load from the GOT slot won't be optimized out by a register load. gcc/ PR target/67400 * Makefile.in (OBJS): Add noplt-symbols.o. * common.opt (fno-plt=): New option. * explow.c (force_reg): Don't set REG_EQUAL if targetm.cannot_set_reg_equal_const returns true. * expr.c (emit_move_insn): Likewise. * noplt-symbols.c: New file. * noplt-symbols.h: Likewise. * target.def (cannot_set_reg_equal_const): New target hook. * toplev.c: Include "noplt-symbols.h". (process_options): Call noplt_symbols_initialize. (toplev::main): Call noplt_symbols_finish. * config/i386/i386-protos.h (ix86_noplt_operand): New. (ix86_noplt_addr_symbol_rtx): Likewise. * config/i386/i386.c: Include "noplt-symbols.h". (ix86_noplt_rtx_p): New function. (ix86_noplt_operand): Likewise. (ix86_noplt_addr_symbol_rtx): Likewise. (ix86_cannot_set_reg_equal_const): Likewise. (ix86_function_ok_for_sibcall): Replace flag_plt with !noplt_decl_p. (ix86_legitimate_address_p): Allow UNSPEC_GOT and UNSPEC_GOTPCREL if ix86_noplt_addr_symbol_rtx doesn't return NULL. (ix86_print_operand_address): Support UNSPEC_GOT and UNSPEC_GOTPCREL if ix86_noplt_addr_symbol_rtx doesn't return NULL. (ix86_expand_move): Load the external function address via the GOT slot if ix86_noplt_operand returns true. (ix86_expand_call): Replace flag_plt and noplt attribute check with !ix86_noplt_rtx_p. (ix86_nopic_noplt_attribute_p): Call ix86_noplt_rtx_p. (TARGET_CANNOT_SET_REG_EQUAL_CONST): New. * config/i386/i386.h (SYMBOL_FLAG_PLT): New. (SYMBOL_REF_PLT_P): Likewise. (SYMBOL_FLAG_NOPLT): Likewise. (SYMBOL_REF_NOPLT_P): Likewise. * doc/tm.texi.in (TARGET_CANNOT_SET_REG_EQUAL_CONST): New hook. * doc/tm.texi: Updated. gcc/testsuite/ PR target/67400 * gcc.target/i386/noplt-5.c: New test. * gcc.target/i386/noplt-6.c: Likweise. * gcc.target/i386/noplt-7.c: Likweise. * gcc.target/i386/noplt-8.c: Likweise. * gcc.target/i386/noplt-9.c: Likweise. * gcc.target/i386/noplt-10.c: Likweise. * gcc.target/i386/noplt-11.c: Likweise. * gcc.target/i386/noplt-12.c: Likweise. * gcc.target/i386/noplt-13.c: Likweise. * gcc.target/i386/noplt-14.c: Likweise. * gcc.target/i386/noplt-15.c: Likweise. * gcc.target/i386/noplt-16.c: Likweise. * gcc.target/i386/noplt-17.c: Likweise. * gcc.target/i386/noplt-18.c: Likweise. * gcc.target/i386/pr67400-1.c: Likweise. * gcc.target/i386/pr67400-2.c: Likewise. * gcc.target/i386/pr67400-3.c: Likewise. * gcc.target/i386/pr67400-4.c: Likewise.
-rw-r--r--gcc/Makefile.in1
-rw-r--r--gcc/common.opt4
-rw-r--r--gcc/config/i386/i386-protos.h3
-rw-r--r--gcc/config/i386/i386.c132
-rw-r--r--gcc/config/i386/i386.h10
-rw-r--r--gcc/doc/tm.texi12
-rw-r--r--gcc/doc/tm.texi.in2
-rw-r--r--gcc/explow.c1
-rw-r--r--gcc/expr.c1
-rw-r--r--gcc/noplt-symbols.c136
-rw-r--r--gcc/noplt-symbols.h31
-rw-r--r--gcc/target.def15
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-10.c15
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-11.c17
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-12.c14
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-13.c14
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-14.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-15.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-16.c18
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-17.c11
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-18.c15
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-5.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-6.c18
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-7.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-8.c18
-rw-r--r--gcc/testsuite/gcc.target/i386/noplt-9.c14
-rw-r--r--gcc/testsuite/gcc.target/i386/pr67400-1.c14
-rw-r--r--gcc/testsuite/gcc.target/i386/pr67400-2.c15
-rw-r--r--gcc/testsuite/gcc.target/i386/pr67400-3.c17
-rw-r--r--gcc/testsuite/gcc.target/i386/pr67400-4.c14
-rw-r--r--gcc/toplev.c4
31 files changed, 605 insertions, 13 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2685b3855c5..0444eff2c48 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1349,6 +1349,7 @@ OBJS = \
mcf.o \
mode-switching.o \
modulo-sched.o \
+ noplt-symbols.o \
omp-low.o \
optabs.o \
optabs-libfuncs.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 6148e49843c..a4ecb956f85 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1816,6 +1816,10 @@ fplt
Common Report Var(flag_plt) Init(1) Optimization
Use PLT for PIC calls (-fno-plt: load the address from GOT at call site).
+fno-plt=
+Common Report RejectNegative Joined Var(noplt_string)
+Load the address from GOT at call site for specific external functions
+
fplugin=
Common Joined RejectNegative Var(common_deferred_options) Defer
Specify a plugin to load.
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef40a2e..05327fc7f28 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -70,6 +70,9 @@ extern bool legitimate_pic_address_disp_p (rtx);
extern void print_reg (rtx, int, FILE*);
extern void ix86_print_operand (FILE *, rtx, int);
+extern bool ix86_noplt_operand (rtx);
+extern rtx ix86_noplt_addr_symbol_rtx (rtx);
+
extern void split_double_mode (machine_mode, rtx[], int, rtx[], rtx[]);
extern const char *output_set_got (rtx, rtx);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index dc0ec0c60ac..ed3c4aeb40c 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -72,6 +72,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-chkp.h"
#include "rtl-chkp.h"
#include "dbgcnt.h"
+#include "noplt-symbols.h"
/* This file should be included last. */
#include "target-def.h"
@@ -6616,6 +6617,61 @@ ix86_target_stack_probe (void)
return TARGET_STACK_PROBE;
}
+
+static bool
+ix86_noplt_rtx_p (rtx call_op)
+{
+ if (SYMBOL_REF_NOPLT_P (call_op))
+ return true;
+
+ if (SYMBOL_REF_PLT_P (call_op))
+ return false;
+
+ tree decl = SYMBOL_REF_DECL (call_op);
+ if (noplt_decl_p (decl)
+ || (!decl && noplt_symbol_p (XSTR (call_op, 0))))
+ {
+ SYMBOL_REF_FLAGS (call_op) |= SYMBOL_FLAG_NOPLT;
+ return true;
+ }
+
+ SYMBOL_REF_FLAGS (call_op) |= SYMBOL_FLAG_PLT;
+ return false;
+}
+
+bool
+ix86_noplt_operand (rtx op)
+{
+ return (!TARGET_PECOFF
+ && !TARGET_MACHO
+ && GET_CODE (op) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (op)
+ && !SYMBOL_REF_LOCAL_P (op)
+ && ix86_noplt_rtx_p (op));
+}
+
+rtx
+ix86_noplt_addr_symbol_rtx (rtx op)
+{
+ if (!TARGET_64BIT
+ && GET_CODE (op) == PLUS
+ && REG_P (XEXP (op, 0)))
+ op = XEXP (op, 1);
+ if (GET_CODE (op) == CONST)
+ {
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == UNSPEC
+ && (XINT (op, 1) == (TARGET_64BIT
+ ? UNSPEC_GOTPCREL
+ : UNSPEC_GOT)))
+ {
+ op = XVECEXP (op, 0, 0);
+ if (ix86_noplt_operand (op))
+ return op;
+ }
+ }
+ return NULL_RTX;
+}
/* Decide whether we can make a sibling call to a function. DECL is the
declaration of the function being targeted by the call and EXP is the
@@ -6633,7 +6689,7 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
if (!TARGET_MACHO
&& !TARGET_64BIT
&& flag_pic
- && flag_plt
+ && !noplt_decl_p (decl)
&& decl && !targetm.binds_local_p (decl))
return false;
@@ -14363,6 +14419,19 @@ ix86_legitimate_constant_p (machine_mode, rtx x)
return true;
}
+/* True if the constant X cannot be used to set REG_EQUAL note. */
+
+static bool
+ix86_cannot_set_reg_equal_const (rtx x)
+{
+ /* External function symbol must be loaded via the GOT slot for
+ -fno-plt. */
+ return (!flag_pic
+ && ix86_cmodel != CM_LARGE
+ && (TARGET_64BIT || HAVE_LD_R_386_GOT32X)
+ && ix86_noplt_operand (x));
+}
+
/* Determine if it's legal to put X into the constant pool. This
is not possible for the address of thread-local symbols, which
is checked above. */
@@ -14730,6 +14799,18 @@ ix86_legitimate_address_p (machine_mode, rtx addr, bool strict)
used. While ABI specify also 32bit relocations, we don't produce
them at all and use IP relative instead. */
case UNSPEC_GOT:
+ gcc_assert (flag_pic
+ || (!flag_pic
+ && ix86_cmodel != CM_LARGE
+ && !TARGET_64BIT
+ && HAVE_LD_R_386_GOT32X
+ && ix86_noplt_addr_symbol_rtx (addr)));
+ if (!TARGET_64BIT)
+ goto is_legitimate_pic;
+
+ /* 64bit address unspec. */
+ return false;
+
case UNSPEC_GOTOFF:
gcc_assert (flag_pic);
if (!TARGET_64BIT)
@@ -14739,6 +14820,13 @@ ix86_legitimate_address_p (machine_mode, rtx addr, bool strict)
return false;
case UNSPEC_GOTPCREL:
+ gcc_assert (flag_pic
+ || (!flag_pic
+ && ix86_cmodel != CM_LARGE
+ && (TARGET_64BIT || HAVE_LD_R_386_GOT32X)
+ && ix86_noplt_addr_symbol_rtx (addr)));
+ goto is_legitimate_pic;
+
case UNSPEC_PCREL:
gcc_assert (flag_pic);
goto is_legitimate_pic;
@@ -17336,6 +17424,13 @@ ix86_print_operand_address (FILE *file, rtx addr)
}
else if (flag_pic)
output_pic_addr_const (file, disp, 0);
+ else if (GET_CODE (disp) == CONST
+ && GET_CODE (XEXP (disp, 0)) == UNSPEC
+ && !flag_pic
+ && ix86_cmodel != CM_LARGE
+ && (TARGET_64BIT || HAVE_LD_R_386_GOT32X)
+ && ix86_noplt_addr_symbol_rtx (addr))
+ output_pic_addr_const (file, XEXP (disp, 0), code);
else
output_addr_const (file, disp);
}
@@ -18593,6 +18688,25 @@ ix86_expand_move (machine_mode mode, rtx operands[])
op1 = convert_to_mode (mode, op1, 1);
}
}
+ }
+ else if (!flag_pic
+ && ix86_cmodel != CM_LARGE
+ && (TARGET_64BIT || HAVE_LD_R_386_GOT32X)
+ && ix86_noplt_operand (op1))
+ {
+ /* Load the external function address via the GOT slot to
+ avoid PLT. */
+ op1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op1),
+ (TARGET_64BIT
+ ? UNSPEC_GOTPCREL
+ : UNSPEC_GOT));
+ op1 = gen_rtx_CONST (Pmode, op1);
+ op1 = gen_const_mem (Pmode, op1);
+ if (op0 == op1)
+ return;
+ op1 = convert_to_mode (mode, op1, 1);
+ if (MEM_P (op0))
+ op1 = force_reg (mode, op1);
}
else
{
@@ -26754,10 +26868,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
&& GET_CODE (addr) == SYMBOL_REF
&& !SYMBOL_REF_LOCAL_P (addr))
{
- if (flag_plt
- && (SYMBOL_REF_DECL (addr) == NULL_TREE
- || !lookup_attribute ("noplt",
- DECL_ATTRIBUTES (SYMBOL_REF_DECL (addr)))))
+ if (!ix86_noplt_rtx_p (addr))
{
if (!TARGET_64BIT
|| (ix86_cmodel == CM_LARGE_PIC
@@ -26909,14 +27020,7 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
|| SYMBOL_REF_LOCAL_P (call_op))
return false;
- tree symbol_decl = SYMBOL_REF_DECL (call_op);
-
- if (!flag_plt
- || (symbol_decl != NULL_TREE
- && lookup_attribute ("noplt", DECL_ATTRIBUTES (symbol_decl))))
- return true;
-
- return false;
+ return ix86_noplt_rtx_p (call_op);
}
/* Output the assembly for a call instruction. */
@@ -53789,6 +53893,8 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
#endif
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM ix86_cannot_force_const_mem
+#undef TARGET_CANNOT_SET_REG_EQUAL_CONST
+#define TARGET_CANNOT_SET_REG_EQUAL_CONST ix86_cannot_set_reg_equal_const
#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
#define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_true
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index be96c75df77..f7a86d2b524 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2561,6 +2561,16 @@ struct GTY(()) machine_function {
#define SYMBOL_REF_STUBVAR_P(X) \
((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_STUBVAR) != 0)
+/* Re-use the SYMBOL_FLAG_DLLIMPORT bit. */
+#define SYMBOL_FLAG_PLT SYMBOL_FLAG_DLLIMPORT
+#define SYMBOL_REF_PLT_P(X) \
+ ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_PLT) != 0)
+
+/* Re-use the SYMBOL_FLAG_DLLEXPORT bit. */
+#define SYMBOL_FLAG_NOPLT SYMBOL_FLAG_DLLEXPORT
+#define SYMBOL_REF_NOPLT_P(X) \
+ ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_NOPLT) != 0)
+
extern void debug_ready_dispatch (void);
extern void debug_dispatch_window (int);
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index bede549976f..d936309341c 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -5577,6 +5577,18 @@ holding the constant. This restriction is often true of addresses
of TLS symbols for various targets.
@end deftypefn
+@deftypefn {Target Hook} bool TARGET_CANNOT_SET_REG_EQUAL_CONST (rtx @var{x})
+This hook should return true if @var{x} is of a form that cannot (or
+should not) be used to set @code{REG_EQUAL} note.
+
+The default version of this hook returns false.
+
+The primary reason to define this hook is to prevent cse and fwprop from
+deciding that a constant should be used to replace a register. This
+restriction is often true for addresses of external function symbols
+when @option{-fno-plt} is used.
+@end deftypefn
+
@deftypefn {Target Hook} bool TARGET_USE_BLOCKS_FOR_CONSTANT_P (machine_mode @var{mode}, const_rtx @var{x})
This hook should return true if pool entries for constant @var{x} can
be placed in an @code{object_block} structure. @var{mode} is the mode
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 167d19a0fcc..d779856cf42 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4201,6 +4201,8 @@ address; but often a machine-dependent strategy can generate better code.
@hook TARGET_CANNOT_FORCE_CONST_MEM
+@hook TARGET_CANNOT_SET_REG_EQUAL_CONST
+
@hook TARGET_USE_BLOCKS_FOR_CONSTANT_P
@hook TARGET_USE_BLOCKS_FOR_DECL_P
diff --git a/gcc/explow.c b/gcc/explow.c
index 807c9260bdb..58dc4a581c2 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -647,6 +647,7 @@ force_reg (machine_mode mode, rtx x)
and that X can be substituted for it. Don't get confused
if INSN set something else (such as a SUBREG of TEMP). */
if (CONSTANT_P (x)
+ && !targetm.cannot_set_reg_equal_const (x)
&& (set = single_set (insn)) != 0
&& SET_DEST (set) == temp
&& ! rtx_equal_p (x, SET_SRC (set)))
diff --git a/gcc/expr.c b/gcc/expr.c
index da688701928..b53533e0bda 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -3593,6 +3593,7 @@ emit_move_insn (rtx x, rtx y)
last_insn = emit_move_insn_1 (x, y);
if (y_cst && REG_P (x)
+ && !targetm.cannot_set_reg_equal_const (y_cst)
&& (set = single_set (last_insn)) != NULL_RTX
&& SET_DEST (set) == x
&& ! rtx_equal_p (y_cst, SET_SRC (set)))
diff --git a/gcc/noplt-symbols.c b/gcc/noplt-symbols.c
new file mode 100644
index 00000000000..efdc2e892a4
--- /dev/null
+++ b/gcc/noplt-symbols.c
@@ -0,0 +1,136 @@
+/* No-plt symbol class for the GNU compiler.
+ Copyright (C) 2015 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "options.h"
+#include "diagnostic.h"
+#include "tree.h"
+#include "target.h"
+#include "noplt-symbols.h"
+
+static noplt_symbol_table_type *noplt_symbols;
+static char *symbols;
+
+void
+noplt_symbols_initialize (void)
+{
+ if (!flag_plt || !noplt_string)
+ return;
+
+ struct stat st;
+ char *symbol;
+ const char *delim;
+ if (stat (noplt_string, &st) < 0)
+ {
+ symbols = xstrdup (noplt_string);
+ delim = ",";
+ }
+ else
+ {
+ if (!S_ISREG (st.st_mode))
+ {
+ error ("%s: invalid no-plt symbol file", noplt_string);
+ return;
+ }
+
+ size_t filesize = st.st_size;
+ FILE *fp = fopen (noplt_string, "rb");
+ if (fp == NULL)
+ {
+ error ("%s: could not open no-plt symbol file", noplt_string);
+ return;
+ }
+ symbols = (char *) xmalloc (filesize + 1);
+ if (fread (symbols, 1, filesize, fp) != filesize)
+ {
+ error ("%s: could not read no-plt symbol file", noplt_string);
+ fclose (fp);
+ return;
+ }
+ fclose (fp);
+ symbols[filesize] = '\0';
+ delim = "\r\n";
+ }
+ noplt_symbols = new noplt_symbol_table_type (10);
+ symbol = strtok (symbols, delim);
+ while (symbol)
+ {
+ const char **slot = noplt_symbols->find_slot (symbol, INSERT);
+ *slot = symbol;
+ symbol = strtok (NULL, delim);
+ }
+}
+
+void
+noplt_symbols_finish (void)
+{
+ if (noplt_symbols)
+ {
+ delete noplt_symbols;
+ noplt_symbols = NULL;
+ }
+ if (symbols)
+ {
+ free (symbols);
+ symbols = NULL;
+ }
+}
+
+bool
+noplt_symbol_p (const char *name)
+{
+ if (noplt_symbols)
+ {
+ name = targetm.strip_name_encoding (name);
+ return noplt_symbols->find_slot (name, NO_INSERT) != NULL;
+ }
+ return false;
+}
+
+bool
+noplt_decl_p (tree decl)
+{
+ if (!flag_plt)
+ return true;
+
+ if (decl == NULL_TREE)
+ return false;
+
+ if (lookup_attribute ("noplt", DECL_ATTRIBUTES (decl)))
+ return true;
+
+ if (noplt_symbols)
+ {
+ const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ name = targetm.strip_name_encoding (name);
+ if (noplt_symbols->find_slot (name, NO_INSERT) != NULL)
+ return true;
+
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
+ {
+ tree alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+ name = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (alias)));
+ return noplt_symbols->find_slot (name, NO_INSERT) != NULL;
+ }
+ }
+
+ return false;
+}
diff --git a/gcc/noplt-symbols.h b/gcc/noplt-symbols.h
new file mode 100644
index 00000000000..b9f7eae6846
--- /dev/null
+++ b/gcc/noplt-symbols.h
@@ -0,0 +1,31 @@
+/* No-plt symbol class for the GNU compiler.
+ Copyright (C) 2015 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/>. */
+
+#ifndef NOPLT_SYMBOLS_H
+#define NOPLT_SYMBOLS_H
+
+/* No-plt symbol hash table type. */
+typedef hash_table<nofree_string_hash> noplt_symbol_table_type;
+
+extern void noplt_symbols_initialize (void);
+extern void noplt_symbols_finish (void);
+extern bool noplt_symbol_p (const char *);
+extern bool noplt_decl_p (tree);
+
+#endif /* NOPLT_SYMBOLS_H */
diff --git a/gcc/target.def b/gcc/target.def
index e8631fdb0a2..15d8ff5ccd7 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2659,6 +2659,21 @@ of TLS symbols for various targets.",
bool, (machine_mode mode, rtx x),
hook_bool_mode_rtx_false)
+/* True if the constant X cannot be used to set REG_EQUAL note. */
+DEFHOOK
+(cannot_set_reg_equal_const,
+ "This hook should return true if @var{x} is of a form that cannot (or\n\
+should not) be used to set @code{REG_EQUAL} note.\n\
+\n\
+The default version of this hook returns false.\n\
+\n\
+The primary reason to define this hook is to prevent cse and fwprop from\n\
+deciding that a constant should be used to replace a register. This\n\
+restriction is often true for addresses of external function symbols\n\
+when @option{-fno-plt} is used.",
+ bool, (rtx x),
+ hook_bool_rtx_false)
+
DEFHOOK_UNDOC
(cannot_copy_insn_p,
"True if the insn @var{x} cannot be duplicated.",
diff --git a/gcc/testsuite/gcc.target/i386/noplt-10.c b/gcc/testsuite/gcc.target/i386/noplt-10.c
new file mode 100644
index 00000000000..a4c316bb91a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-10.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar (void);
+extern void *p;
+
+void
+foo (void)
+{
+ p = &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { { ! ia32 } || r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-11.c b/gcc/testsuite/gcc.target/i386/noplt-11.c
new file mode 100644
index 00000000000..9e4006a11ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-11.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+static void
+bar (void)
+{
+}
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-12.c b/gcc/testsuite/gcc.target/i386/noplt-12.c
new file mode 100644
index 00000000000..ce2f01020d3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-12.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern int bar;
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-13.c b/gcc/testsuite/gcc.target/i386/noplt-13.c
new file mode 100644
index 00000000000..2113e82e18f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-13.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar (void);
+
+int
+__attribute__((regparm(1)))
+foo (void *p)
+{
+ return p == &bar;
+}
+
+/* { dg-final { scan-assembler "cmp\(l|q\)\[ \t\]*bar@GOTPCREL" { target { { ! ia32 } && r_x86_64_gotpcrelx } } } } */
+/* { dg-final { scan-assembler "cmpl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-14.c b/gcc/testsuite/gcc.target/i386/noplt-14.c
new file mode 100644
index 00000000000..ee0217abe78
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-14.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar1 (void) asm ("bar");
+
+void
+foo (void)
+{
+ bar1 ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT" { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-15.c b/gcc/testsuite/gcc.target/i386/noplt-15.c
new file mode 100644
index 00000000000..4fcb460b674
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-15.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar (void) asm ("bar1");
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar1@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar1@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-16.c b/gcc/testsuite/gcc.target/i386/noplt-16.c
new file mode 100644
index 00000000000..5c70161bb15
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-16.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt -fno-plt=bar" } */
+
+extern void bar (void);
+extern void yyy (void);
+
+int
+foo (void)
+{
+ bar ();
+ yyy ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.yyy@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT" { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.yyy@GOT" { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-17.c b/gcc/testsuite/gcc.target/i386/noplt-17.c
new file mode 100644
index 00000000000..16083ed2c01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-17.c
@@ -0,0 +1,11 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-popcnt -fno-pic -fno-plt=__popcountsi2,__popcountdi2" } */
+
+int
+foo (unsigned int i)
+{
+ return __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.__popcountdi2@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.__popcountsi2@GOT" { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-18.c b/gcc/testsuite/gcc.target/i386/noplt-18.c
new file mode 100644
index 00000000000..d3e3eb4b1ee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-18.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern int bar (void);
+static __typeof(bar) __bar __attribute__ ((__weakref__("bar")));
+
+int
+active_p (void)
+{
+ static void *const active_ptr = __extension__ (void *) &__bar;
+ return active_ptr != 0;
+}
+
+/* { dg-final { scan-assembler "__bar@GOTPCREL" { target { { ! ia32 } && r_x86_64_gotpcrelx } } } } */
+/* { dg-final { scan-assembler "__bar@GOT" { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-5.c b/gcc/testsuite/gcc.target/i386/noplt-5.c
new file mode 100644
index 00000000000..3c7331a215a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-5.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT" { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-6.c b/gcc/testsuite/gcc.target/i386/noplt-6.c
new file mode 100644
index 00000000000..b61086b34bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=xxx,bar" } */
+
+extern void bar (void);
+extern void yyy (void);
+
+int
+foo (void)
+{
+ bar ();
+ yyy ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.yyy@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT" { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.yyy@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-7.c b/gcc/testsuite/gcc.target/i386/noplt-7.c
new file mode 100644
index 00000000000..33600cbc2e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-7.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt=bar" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-8.c b/gcc/testsuite/gcc.target/i386/noplt-8.c
new file mode 100644
index 00000000000..e8a3e81e3f3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-8.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt=xxx,bar" } */
+
+extern void bar (void);
+extern void yyy (void);
+
+int
+foo (void)
+{
+ bar ();
+ yyy ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.yyy@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.yyy@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-9.c b/gcc/testsuite/gcc.target/i386/noplt-9.c
new file mode 100644
index 00000000000..a9598fe01f4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-9.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt=bar" } */
+
+extern void bar (void);
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { { ! ia32 } || r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67400-1.c b/gcc/testsuite/gcc.target/i386/pr67400-1.c
new file mode 100644
index 00000000000..1b8dfbcf6e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr67400-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern void bar (void);
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { { ! ia32 } || r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67400-2.c b/gcc/testsuite/gcc.target/i386/pr67400-2.c
new file mode 100644
index 00000000000..aa78748491a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr67400-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern void bar (void);
+extern void *p;
+
+void
+foo (void)
+{
+ p = &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { { ! ia32 } || r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67400-3.c b/gcc/testsuite/gcc.target/i386/pr67400-3.c
new file mode 100644
index 00000000000..b303213f7fd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr67400-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+static void
+bar (void)
+{
+}
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67400-4.c b/gcc/testsuite/gcc.target/i386/pr67400-4.c
new file mode 100644
index 00000000000..b2364cf198d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr67400-4.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern int bar;
+
+void *
+foo (void)
+{
+ return &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && r_386_got32x } } } } */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 0083d816701..ac1be00bf7d 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3. If not see
#include "optabs-libfuncs.h"
#include "tree-chkp.h"
#include "omp-low.h"
+#include "noplt-symbols.h"
#if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
#include "dbxout.h"
@@ -1226,6 +1227,8 @@ process_options (void)
/* Some machines may reject certain combinations of options. */
targetm.target_option.override ();
+ noplt_symbols_initialize ();
+
/* Avoid any informative notes in the second run of -fcompare-debug. */
if (flag_compare_debug)
diagnostic_inhibit_notes (global_dc);
@@ -2087,6 +2090,7 @@ toplev::main (int argc, char **argv)
diagnostic_finish (global_dc);
+ noplt_symbols_finish ();
finalize_plugins ();
location_adhoc_data_fini (line_table);
if (seen_error () || werrorcount)