Optimize i386 syscall inlining for GCC 5

Since GCC 5 and above can properly spill %ebx when needed, we can inline
syscalls with 6 arguments if GCC 5 or above is used to compile glibc.
This patch rewrites INTERNAL_SYSCALL macros and skips __libc_do_syscall
for GCC 5.

For sysdeps/unix/sysv/linux/i386/brk.c, with -O2 -march=i686
-mtune=generic, GCC 5.2 now generates:

<__brk>:
   0:	push   %ebx
   1:	mov    $0x2d,%eax
   6:	mov    0x8(%esp),%ebx
   a:	call   b <__brk+0xb>	b: R_386_PC32	__x86.get_pc_thunk.dx
   f:	add    $0x2,%edx	11: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
  15:	call   *%gs:0x10
  1c:	mov    0x0(%edx),%edx	1e: R_386_GOT32	__curbrk
  22:	cmp    %eax,%ebx
  24:	mov    %eax,(%edx)
  26:	ja     30 <__brk+0x30>
  28:	xor    %eax,%eax
  2a:	pop    %ebx
  2b:	ret

instead of

<__brk>:
   0:	push   %ebx
   1:	mov    0x8(%esp),%ecx
   5:	call   6 <__brk+0x6>	6: R_386_PC32	__x86.get_pc_thunk.bx
   a:	add    $0x2,%ebx	c: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
  10:	xchg   %ecx,%ebx
  12:	mov    $0x2d,%eax
  17:	call   *%gs:0x10
  1e:	xchg   %ecx,%ebx
  20:	mov    %eax,%edx
  22:	mov    0x0(%ebx),%eax	24: R_386_GOT32	__curbrk
  28:	mov    %edx,(%eax)
  2a:	xor    %eax,%eax
  2c:	cmp    %edx,%ecx
  2e:	ja     38 <__brk+0x38>
  30:	pop    %ebx
  31:	ret

The new one is shorter by 2 instructions.

	* sysdeps/unix/sysv/linux/i386/libc-do-syscall.S
	(__libc_do_syscall): Defined only if !__GNUC_PREREQ (5,0).
	* sysdeps/unix/sysv/linux/i386/sysdep.h: Define assembler macros
	only if !__GNUC_PREREQ (5,0).
	(INTERNAL_SYSCALL_MAIN_6): Optimize for GCC 5.
	(INTERNAL_SYSCALL_MAIN_INLINE): Likewise.
	(INTERNAL_SYSCALL_NCS): Likewise.
	(LOADREGS_0): New macro for GCC 5.
	(ASMARGS_0): Likewise.
	(LOADREGS_1): Likewise.
	(ASMARGS_1): Likewise.
	(LOADREGS_2): Likewise.
	(ASMARGS_2): Likewise.
	(LOADREGS_3): Likewise.
	(ASMARGS_3): Likewise.
	(LOADREGS_4): Likewise.
	(ASMARGS_4): Likewise.
	(LOADREGS_5): Likewise.
	(ASMARGS_5): Likewise.
	(LOADREGS_6): Likewise.
	(ASMARGS_6): Likewise.
This commit is contained in:
H.J. Lu 2015-10-15 05:23:42 -07:00
parent 83c01ab32b
commit 98ad631cd0
3 changed files with 149 additions and 29 deletions

View File

@ -1,3 +1,27 @@
2015-10-15 H.J. Lu <hongjiu.lu@intel.com>
* sysdeps/unix/sysv/linux/i386/libc-do-syscall.S
(__libc_do_syscall): Defined only if !__GNUC_PREREQ (5,0).
* sysdeps/unix/sysv/linux/i386/sysdep.h: Define assembler macros
only if !__GNUC_PREREQ (5,0).
(INTERNAL_SYSCALL_MAIN_6): Optimize for GCC 5.
(INTERNAL_SYSCALL_MAIN_INLINE): Likewise.
(INTERNAL_SYSCALL_NCS): Likewise.
(LOADREGS_0): New macro for GCC 5.
(ASMARGS_0): Likewise.
(LOADREGS_1): Likewise.
(ASMARGS_1): Likewise.
(LOADREGS_2): Likewise.
(ASMARGS_2): Likewise.
(LOADREGS_3): Likewise.
(ASMARGS_3): Likewise.
(LOADREGS_4): Likewise.
(ASMARGS_4): Likewise.
(LOADREGS_5): Likewise.
(ASMARGS_5): Likewise.
(LOADREGS_6): Likewise.
(ASMARGS_6): Likewise.
2015-10-15 H.J. Lu <hongjiu.lu@intel.com> 2015-10-15 H.J. Lu <hongjiu.lu@intel.com>
* sysdeps/unix/sysv/linux/i386/Makefile (CFLAGS-mmap.c): Add * sysdeps/unix/sysv/linux/i386/Makefile (CFLAGS-mmap.c): Add

View File

@ -18,6 +18,8 @@
#include <sysdep.h> #include <sysdep.h>
#if !__GNUC_PREREQ (5,0)
/* %eax, %ecx, %edx and %esi contain the values expected by the kernel. /* %eax, %ecx, %edx and %esi contain the values expected by the kernel.
%edi points to a structure with the values of %ebx, %edi and %ebp. */ %edi points to a structure with the values of %ebx, %edi and %ebp. */
@ -48,3 +50,4 @@ ENTRY (__libc_do_syscall)
cfi_restore (ebx) cfi_restore (ebx)
ret ret
END (__libc_do_syscall) END (__libc_do_syscall)
#endif

View File

@ -227,6 +227,7 @@
extern int __syscall_error (int) extern int __syscall_error (int)
attribute_hidden __attribute__ ((__regparm__ (1))); attribute_hidden __attribute__ ((__regparm__ (1)));
#if !__GNUC_PREREQ (5,0)
/* We need some help from the assembler to generate optimal code. We /* We need some help from the assembler to generate optimal code. We
define some macros here which later will be used. */ define some macros here which later will be used. */
asm (".L__X'%ebx = 1\n\t" asm (".L__X'%ebx = 1\n\t"
@ -266,6 +267,7 @@ struct libc_do_syscall_args
{ {
int ebx, edi, ebp; int ebx, edi, ebp;
}; };
#endif
/* Define a macro which expands inline into the wrapper code for a system /* Define a macro which expands inline into the wrapper code for a system
call. */ call. */
@ -322,7 +324,11 @@ struct libc_do_syscall_args
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args) INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args)
/* Each object using 6-argument inline syscalls must include a /* Each object using 6-argument inline syscalls must include a
definition of __libc_do_syscall. */ definition of __libc_do_syscall. */
#define INTERNAL_SYSCALL_MAIN_6(name, err, arg1, arg2, arg3, \ #if __GNUC_PREREQ (5,0)
# define INTERNAL_SYSCALL_MAIN_6(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 6, args)
#else /* GCC 5 */
# define INTERNAL_SYSCALL_MAIN_6(name, err, arg1, arg2, arg3, \
arg4, arg5, arg6) \ arg4, arg5, arg6) \
struct libc_do_syscall_args _xv = \ struct libc_do_syscall_args _xv = \
{ \ { \
@ -336,12 +342,50 @@ struct libc_do_syscall_args
: "=a" (resultvar) \ : "=a" (resultvar) \
: "i" (__NR_##name), "c" (arg2), "d" (arg3), "S" (arg4), "D" (&_xv) \ : "i" (__NR_##name), "c" (arg2), "d" (arg3), "S" (arg4), "D" (&_xv) \
: "memory", "cc") : "memory", "cc")
#endif /* GCC 5 */
#define INTERNAL_SYSCALL(name, err, nr, args...) \ #define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \ ({ \
register unsigned int resultvar; \ register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \ INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \
(int) resultvar; }) (int) resultvar; })
#ifdef I386_USE_SYSENTER #ifdef I386_USE_SYSENTER
# if __GNUC_PREREQ (5,0)
# ifdef SHARED
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"call *%%gs:%P2" \
: "=a" (resultvar) \
: "a" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"call *%%gs:%P2" \
: "=a" (resultvar) \
: "a" (name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# else
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"call *_dl_sysinfo" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"call *_dl_sysinfo" \
: "=a" (resultvar) \
: "a" (name) ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# endif
# else /* GCC 5 */
# ifdef SHARED # ifdef SHARED
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \ # define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
EXTRAVAR_##nr \ EXTRAVAR_##nr \
@ -387,7 +431,25 @@ struct libc_do_syscall_args
: "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \ : "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; }) (int) resultvar; })
# endif # endif
# endif /* GCC 5 */
#else #else
# if __GNUC_PREREQ (5,0)
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"int $0x80" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
# define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
LOADREGS_##nr(args) \
asm volatile ( \
"int $0x80" \
: "=a" (resultvar) \
: "a" (name) ASMARGS_##nr(args) : "memory", "cc"); \
(int) resultvar; })
# else /* GCC 5 */
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \ # define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
EXTRAVAR_##nr \ EXTRAVAR_##nr \
asm volatile ( \ asm volatile ( \
@ -408,6 +470,7 @@ struct libc_do_syscall_args
: "=a" (resultvar) \ : "=a" (resultvar) \
: "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \ : "0" (name) ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; }) (int) resultvar; })
# endif /* GCC 5 */
#endif #endif
#undef INTERNAL_SYSCALL_DECL #undef INTERNAL_SYSCALL_DECL
@ -472,6 +535,36 @@ struct libc_do_syscall_args
# define RESTOREARGS_5 # define RESTOREARGS_5
#endif #endif
#if __GNUC_PREREQ (5,0)
# define LOADREGS_0()
# define ASMARGS_0()
# define LOADREGS_1(arg1) \
LOADREGS_0 ()
# define ASMARGS_1(arg1) \
ASMARGS_0 (), "b" ((unsigned int) (arg1))
# define LOADREGS_2(arg1, arg2) \
LOADREGS_1 (arg1)
# define ASMARGS_2(arg1, arg2) \
ASMARGS_1 (arg1), "c" ((unsigned int) (arg2))
# define LOADREGS_3(arg1, arg2, arg3) \
LOADREGS_2 (arg1, arg2)
# define ASMARGS_3(arg1, arg2, arg3) \
ASMARGS_2 (arg1, arg2), "d" ((unsigned int) (arg3))
# define LOADREGS_4(arg1, arg2, arg3, arg4) \
LOADREGS_3 (arg1, arg2, arg3)
# define ASMARGS_4(arg1, arg2, arg3, arg4) \
ASMARGS_3 (arg1, arg2, arg3), "S" ((unsigned int) (arg4))
# define LOADREGS_5(arg1, arg2, arg3, arg4, arg5) \
LOADREGS_4 (arg1, arg2, arg3, arg4)
# define ASMARGS_5(arg1, arg2, arg3, arg4, arg5) \
ASMARGS_4 (arg1, arg2, arg3, arg4), "D" ((unsigned int) (arg5))
# define LOADREGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \
register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); \
LOADREGS_5 (arg1, arg2, arg3, arg4, arg5)
# define ASMARGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \
ASMARGS_5 (arg1, arg2, arg3, arg4, arg5), "r" (_a6)
#endif /* GCC 5 */
#define ASMFMT_0() #define ASMFMT_0()
#ifdef __PIC__ #ifdef __PIC__
# define ASMFMT_1(arg1) \ # define ASMFMT_1(arg1) \