From 55ab339cc4173565095b66c0fc2ffa4267b55606 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 28 Aug 2015 19:14:49 -0700
Subject: [PATCH] x86-64: Load external function address via GOT slot
This patch implements the alternate code sequence recommended in
https://groups.google.com/forum/#!topic/x86-64-abi/de5_KnLHxtI
to load external function address via GOT slot with
movq func@GOTPCREL(%rip), %rax
so that linker won't create an PLT entry for extern function address.
gcc/
PR target/67400
* config/i386/i386-protos.h (ix86_force_load_from_GOT_p): New.
* config/i386/i386.c (ix86_force_load_from_GOT_p): New function.
(ix86_legitimate_address_p): Allow UNSPEC_GOTPCREL if
ix86_force_load_from_GOT_p returns true.
(ix86_print_operand_address): Support UNSPEC_GOTPCREL if
ix86_force_load_from_GOT_p returns true.
(ix86_expand_move): Load the external function address via the
GOT slot if ix86_force_load_from_GOT_p returns true.
* config/i386/i386.md (*movsi_internal): Replace general_operand
with ix86_general_operand.
(*movqi_internal): Likewise.
* config/i386/predicates.md (x86_64_immediate_operand): Return
false if ix86_force_load_from_GOT_p returns true.
(address_no_seg_operand): Likewise.
(ix86_general_operand): New predicate.
gcc/testsuite/
PR target/67400
* gcc.target/i386/pr67400-1.c: New test.
* gcc.target/i386/pr67400-2.c: Likewise.
* gcc.target/i386/pr67400-3.c: Likewise.
* gcc.target/i386/pr67400-4.c: Likewise.
* gcc.target/i386/pr67400-5.c: Likewise.
* gcc.target/i386/pr67400-6.c: Likewise.
---
gcc/config/i386/i386-protos.h | 1 +
gcc/config/i386/i386.c | 44 +++++++++++++++++++++++++++++++
gcc/config/i386/i386.md | 4 +--
gcc/config/i386/predicates.md | 15 +++++++++++
gcc/testsuite/gcc.target/i386/pr67400-1.c | 13 +++++++++
gcc/testsuite/gcc.target/i386/pr67400-2.c | 14 ++++++++++
gcc/testsuite/gcc.target/i386/pr67400-3.c | 16 +++++++++++
gcc/testsuite/gcc.target/i386/pr67400-4.c | 13 +++++++++
gcc/testsuite/gcc.target/i386/pr67400-5.c | 11 ++++++++
gcc/testsuite/gcc.target/i386/pr67400-6.c | 13 +++++++++
10 files changed, 142 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-1.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-3.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-4.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-5.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr67400-6.c
@@ -70,6 +70,7 @@ extern bool ix86_expand_set_or_movmem (rtx, rtx, rtx, rtx, rtx, rtx,
extern bool constant_address_p (rtx);
extern bool legitimate_pic_operand_p (rtx);
extern bool legitimate_pic_address_disp_p (rtx);
+extern bool ix86_force_load_from_GOT_p (rtx);
extern void print_reg (rtx, int, FILE*);
extern void ix86_print_operand (FILE *, rtx, int);
@@ -15182,6 +15182,24 @@ ix86_legitimate_constant_p (machine_mode mode, rtx x)
return true;
}
+/* True if operand X should be loaded from GOT. */
+
+bool
+ix86_force_load_from_GOT_p (rtx x)
+{
+ /* External function symbol should be loaded via the GOT slot for
+ -fno-plt. */
+ return (!flag_plt
+ && !flag_pic
+ && ix86_cmodel != CM_LARGE
+ && TARGET_64BIT
+ && !TARGET_PECOFF
+ && !TARGET_MACHO
+ && GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (x)
+ && !SYMBOL_REF_LOCAL_P (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. */
@@ -15560,6 +15578,10 @@ ix86_legitimate_address_p (machine_mode, rtx addr, bool strict)
return false;
case UNSPEC_GOTPCREL:
+ gcc_assert (flag_pic
+ || ix86_force_load_from_GOT_p (XVECEXP (XEXP (disp, 0), 0, 0)));
+ goto is_legitimate_pic;
+
case UNSPEC_PCREL:
gcc_assert (flag_pic);
goto is_legitimate_pic;
@@ -18130,6 +18152,12 @@ ix86_print_operand_address_as (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
+ && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOTPCREL
+ || XINT (XEXP (disp, 0), 1) == UNSPEC_GOT)
+ && ix86_force_load_from_GOT_p (XVECEXP (XEXP (disp, 0), 0, 0)))
+ output_pic_addr_const (file, XEXP (disp, 0), code);
else
output_addr_const (file, disp);
}
@@ -19448,6 +19476,22 @@ ix86_expand_move (machine_mode mode, rtx operands[])
op1 = convert_to_mode (mode, op1, 1);
}
}
+ }
+ else if (ix86_force_load_from_GOT_p (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);
+ /* This symbol must be referenced via a load from the Global
+ Offset Table. */
+ set_mem_alias_set (op1, ix86_GOT_alias_set ());
+ op1 = convert_to_mode (mode, op1, 1);
+ op1 = force_reg (mode, op1);
}
else
{
@@ -2347,7 +2347,7 @@
(define_insn "*movsi_internal"
[(set (match_operand:SI 0 "nonimmediate_operand"
"=r,m ,*y,*y,?rm,?*y,*v,*v,*v,m ,?r ,?r,?*Yi,*k ,*rm")
- (match_operand:SI 1 "general_operand"
+ (match_operand:SI 1 "ix86_general_operand"
"g ,re,C ,*y,*y ,rm ,C ,*v,m ,*v,*Yj,*v,r ,*krm,*k"))]
"!(MEM_P (operands[0]) && MEM_P (operands[1]))"
{
@@ -2564,7 +2564,7 @@
(define_insn "*movqi_internal"
[(set (match_operand:QI 0 "nonimmediate_operand"
"=q,q ,q ,r,r ,?r,m ,k,k,r ,m,k")
- (match_operand:QI 1 "general_operand"
+ (match_operand:QI 1 "ix86_general_operand"
"q ,qn,qm,q,rn,qm,qn,r ,k,k,k,m"))]
"!(MEM_P (operands[0]) && MEM_P (operands[1]))"
{
@@ -149,6 +149,10 @@
(define_predicate "x86_64_immediate_operand"
(match_code "const_int,symbol_ref,label_ref,const")
{
+ /* Load the external function address via the GOT slot to avoid PLT. */
+ if (ix86_force_load_from_GOT_p (op))
+ return false;
+
if (!TARGET_64BIT)
return immediate_operand (op, mode);
@@ -363,6 +367,13 @@
}
})
+;; Return true if OP is general operand, excluding the external function
+;; symbol if it should be loaded via the GOT slot to avoid PLT.
+(define_predicate "ix86_general_operand"
+ (and (match_operand 0 "general_operand")
+ (ior (not (match_code "symbol_ref"))
+ (match_test "!ix86_force_load_from_GOT_p (op)"))))
+
;; Return true if size of VALUE can be stored in a sign
;; extended immediate field.
(define_predicate "x86_64_immediate_size_operand"
@@ -1032,6 +1043,10 @@
struct ix86_address parts;
int ok;
+ /* Load the external function address via the GOT slot to avoid PLT. */
+ if (ix86_force_load_from_GOT_p (op))
+ return false;
+
if (!CONST_INT_P (op)
&& mode != VOIDmode
&& GET_MODE (op) != mode)
new file mode 100644
@@ -0,0 +1,13 @@
+/* { 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-not "\(mov|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar," { target { ! ia32 } } } } */
new file mode 100644
@@ -0,0 +1,14 @@
+/* { 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-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */
new file mode 100644
@@ -0,0 +1,16 @@
+/* { 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 } } } } */
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern void bar (void) __attribute__ ((visibility ("hidden")));
+
+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 } } } } */
new file mode 100644
@@ -0,0 +1,11 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern void foo (void);
+extern void bar (int, int, int, int, int, int, void *);
+
+void
+x (void)
+{
+ bar (1, 2, 3, 4, 5, 6, foo);
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern int bar (void);
+
+int
+check (void *p)
+{
+ return p != &bar;
+}
+
+/* { dg-final { scan-assembler "cmp\(l|q\)\[ \t\]*.*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */
--
2.5.5