00db10
From 0d3555b9b4d5cefe116c32bfa38ac70f1d6c25cb Mon Sep 17 00:00:00 2001
00db10
From: Carlos Eduardo Seo <cseo@linux.vnet.ibm.com>
00db10
Date: Wed, 11 Nov 2015 17:31:28 -0200
00db10
Subject: [PATCH] powerpc: Optimization for strlen for POWER8.
00db10
00db10
This implementation takes advantage of vectorization to improve performance of
00db10
the loop over the current strlen implementation for POWER7.
00db10
00db10
(cherry picked from commit 1b045ee53e0b8bed75745b931b33f27d21c9ed22)
00db10
---
00db10
 ChangeLog                                          |  13 +
00db10
 sysdeps/powerpc/powerpc64/multiarch/Makefile       |   2 +-
00db10
 .../powerpc/powerpc64/multiarch/ifunc-impl-list.c  |   2 +
00db10
 .../powerpc/powerpc64/multiarch/strlen-power8.S    |  39 +++
00db10
 sysdeps/powerpc/powerpc64/multiarch/strlen.c       |   9 +-
00db10
 sysdeps/powerpc/powerpc64/power8/strlen.S          | 297 +++++++++++++++++++++
00db10
 6 files changed, 358 insertions(+), 4 deletions(-)
00db10
 create mode 100644 sysdeps/powerpc/powerpc64/multiarch/strlen-power8.S
00db10
 create mode 100644 sysdeps/powerpc/powerpc64/power8/strlen.S
00db10
00db10
diff --git a/ChangeLog b/ChangeLog
00db10
index f030b68..e7ea58a 100644
00db10
diff --git a/sysdeps/powerpc/powerpc64/multiarch/Makefile b/sysdeps/powerpc/powerpc64/multiarch/Makefile
00db10
index 7ed56bf..57abe8f 100644
00db10
--- a/sysdeps/powerpc/powerpc64/multiarch/Makefile
00db10
+++ b/sysdeps/powerpc/powerpc64/multiarch/Makefile
00db10
@@ -20,7 +20,7 @@ sysdep_routines += memcpy-power7 memcpy-a2 memcpy-power6 memcpy-cell \
00db10
                   strncpy-power8 strncpy-power7 strncpy-ppc64 \
00db10
                   strncat-power7 \
00db10
                   strstr-power7 strstr-ppc64 \
00db10
-                  strspn-power8 strspn-ppc64 \
00db10
+                  strspn-power8 strspn-ppc64 strlen-power8 \
00db10
                   rawmemchr-ppc64 strlen-power7 strlen-ppc64 strnlen-power7 \
00db10
                   strnlen-ppc64 strcasecmp-power7 strcasecmp_l-power7 \
00db10
                   strncase-power7 strncase_l-power7 \
00db10
diff --git a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
00db10
index f6c70ba..583885c 100644
00db10
--- a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
00db10
+++ b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
00db10
@@ -101,6 +101,8 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
00db10
 
00db10
   /* Support sysdeps/powerpc/powerpc64/multiarch/strlen.c.  */
00db10
   IFUNC_IMPL (i, name, strlen,
00db10
+             IFUNC_IMPL_ADD (array, i, strlen, hwcap2 & PPC_FEATURE2_ARCH_2_07,
00db10
+                             __strlen_power8)
00db10
              IFUNC_IMPL_ADD (array, i, strlen, hwcap & PPC_FEATURE_HAS_VSX,
00db10
                              __strlen_power7)
00db10
              IFUNC_IMPL_ADD (array, i, strlen, 1,
00db10
diff --git a/sysdeps/powerpc/powerpc64/multiarch/strlen-power8.S b/sysdeps/powerpc/powerpc64/multiarch/strlen-power8.S
00db10
new file mode 100644
00db10
index 0000000..686dc3d
00db10
--- /dev/null
00db10
+++ b/sysdeps/powerpc/powerpc64/multiarch/strlen-power8.S
00db10
@@ -0,0 +1,39 @@
00db10
+/* Optimized strlen implementation for POWER8.
00db10
+   Copyright (C) 2016 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <sysdep.h>
00db10
+
00db10
+#undef EALIGN
00db10
+#define EALIGN(name, alignt, words)				\
00db10
+  .section ".text";						\
00db10
+  ENTRY_2(__strlen_power8)					\
00db10
+  .align ALIGNARG(alignt);					\
00db10
+  EALIGN_W_##words;						\
00db10
+  BODY_LABEL(__strlen_power8):					\
00db10
+  cfi_startproc;						\
00db10
+  LOCALENTRY(__strlen_power8)
00db10
+#undef END
00db10
+#define END(name)						\
00db10
+  cfi_endproc;							\
00db10
+  TRACEBACK(__strlen_power8)					\
00db10
+  END_2(__strlen_power8)
00db10
+
00db10
+#undef libc_hidden_builtin_def
00db10
+#define libc_hidden_builtin_def(name)
00db10
+
00db10
+#include <sysdeps/powerpc/powerpc64/power8/strlen.S>
00db10
diff --git a/sysdeps/powerpc/powerpc64/multiarch/strlen.c b/sysdeps/powerpc/powerpc64/multiarch/strlen.c
00db10
index 79a53d9..4b400a5 100644
00db10
--- a/sysdeps/powerpc/powerpc64/multiarch/strlen.c
00db10
+++ b/sysdeps/powerpc/powerpc64/multiarch/strlen.c
00db10
@@ -29,11 +29,14 @@ extern __typeof (__redirect_strlen) __libc_strlen;
00db10
 
00db10
 extern __typeof (__redirect_strlen) __strlen_ppc attribute_hidden;
00db10
 extern __typeof (__redirect_strlen) __strlen_power7 attribute_hidden;
00db10
+extern __typeof (__redirect_strlen) __strlen_power8 attribute_hidden;
00db10
 
00db10
 libc_ifunc (__libc_strlen,
00db10
-            (hwcap & PPC_FEATURE_HAS_VSX)
00db10
-            ? __strlen_power7
00db10
-            : __strlen_ppc);
00db10
+	    (hwcap2 & PPC_FEATURE2_ARCH_2_07)
00db10
+	    ? __strlen_power8 :
00db10
+	      (hwcap & PPC_FEATURE_HAS_VSX)
00db10
+	      ? __strlen_power7
00db10
+	      : __strlen_ppc);
00db10
 
00db10
 #undef strlen
00db10
 strong_alias (__libc_strlen, strlen)
00db10
diff --git a/sysdeps/powerpc/powerpc64/power8/strlen.S b/sysdeps/powerpc/powerpc64/power8/strlen.S
00db10
new file mode 100644
00db10
index 0000000..0142747
00db10
--- /dev/null
00db10
+++ b/sysdeps/powerpc/powerpc64/power8/strlen.S
00db10
@@ -0,0 +1,297 @@
00db10
+/* Optimized strlen implementation for PowerPC64/POWER8 using a vectorized
00db10
+   loop.
00db10
+   Copyright (C) 2016 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <sysdep.h>
00db10
+
00db10
+/* TODO: change these to the actual instructions when the minimum required
00db10
+   binutils allows it.  */
00db10
+#define MFVRD(r,v)	.long (0x7c000067 | ((v)<<(32-11)) | ((r)<<(32-16)))
00db10
+#define VBPERMQ(t,a,b)	.long (0x1000054c \
00db10
+			       | ((t)<<(32-11))	\
00db10
+			       | ((a)<<(32-16))	\
00db10
+			       | ((b)<<(32-21)) )
00db10
+
00db10
+/* int [r3] strlen (char *s [r3])  */
00db10
+
00db10
+/* TODO: change this to .machine power8 when the minimum required binutils
00db10
+   allows it.  */
00db10
+	.machine  power7
00db10
+EALIGN (strlen, 4, 0)
00db10
+	CALL_MCOUNT 1
00db10
+	dcbt	0,r3
00db10
+	clrrdi	r4,r3,3	      /* Align the address to doubleword boundary.  */
00db10
+	rlwinm	r6,r3,3,26,28 /* Calculate padding.  */
00db10
+	li	r0,0	      /* Doubleword with null chars to use
00db10
+				 with cmpb.  */
00db10
+	li	r5,-1	      /* MASK = 0xffffffffffffffff.  */
00db10
+	ld	r12,0(r4)     /* Load doubleword from memory.  */
00db10
+#ifdef __LITTLE_ENDIAN__
00db10
+	sld	r5,r5,r6
00db10
+#else
00db10
+	srd	r5,r5,r6      /* MASK = MASK >> padding.  */
00db10
+#endif
00db10
+	orc	r9,r12,r5     /* Mask bits that are not part of the string.  */
00db10
+	cmpb	r10,r9,r0     /* Check for null bytes in DWORD1.  */
00db10
+	cmpdi	cr7,r10,0     /* If r10 == 0, no null's have been found.  */
00db10
+	bne	cr7,L(done)
00db10
+
00db10
+	/* For shorter strings (< 64 bytes), we will not use vector registers,
00db10
+	   as the overhead isn't worth it.  So, let's use GPRs instead.  This
00db10
+	   will be done the same way as we do in the POWER7 implementation.
00db10
+	   Let's see if we are aligned to a quadword boundary.  If so, we can
00db10
+	   jump to the first (non-vectorized) loop.  Otherwise, we have to
00db10
+	   handle the next DWORD first.  */
00db10
+	mtcrf	0x01,r4
00db10
+	mr	r9,r4
00db10
+	addi	r9,r9,8
00db10
+	bt	28,L(align64)
00db10
+
00db10
+	/* Handle the next 8 bytes so we are aligned to a quadword
00db10
+	   boundary.  */
00db10
+	ldu	r5,8(r4)
00db10
+	cmpb	r10,r5,r0
00db10
+	cmpdi	cr7,r10,0
00db10
+	addi	r9,r9,8
00db10
+	bne	cr7,L(done)
00db10
+
00db10
+L(align64):
00db10
+	/* Proceed to the old (POWER7) implementation, checking two doublewords
00db10
+	   per iteraction.  For the first 56 bytes, we will just check for null
00db10
+	   characters.  After that, we will also check if we are 64-byte aligned
00db10
+	   so we can jump to the vectorized implementation.  We will unroll
00db10
+	   these loops to avoid excessive branching.  */
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	/* Are we 64-byte aligned? If so, jump to the vectorized loop.
00db10
+	   Note: aligning to 64-byte will necessarily slow down performance for
00db10
+	   strings around 64 bytes in length due to the extra comparisons
00db10
+	   required to check alignment for the vectorized loop.  This is a
00db10
+	   necessary tradeoff we are willing to take in order to speed up the
00db10
+	   calculation for larger strings.  */
00db10
+	andi.	r10,r9,63
00db10
+	beq	cr0,L(preloop)
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	andi.	r10,r9,63
00db10
+	beq	cr0,L(preloop)
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	andi.	r10,r9,63
00db10
+	beq	cr0,L(preloop)
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+	bne	cr7,L(dword_zero)
00db10
+
00db10
+	andi.	r10,r9,63
00db10
+	beq	cr0,L(preloop)
00db10
+	ld	r6,8(r4)
00db10
+	ldu	r5,16(r4)
00db10
+	cmpb	r10,r6,r0
00db10
+	cmpb	r11,r5,r0
00db10
+	or	r5,r10,r11
00db10
+	cmpdi	cr7,r5,0
00db10
+	addi	r9,r9,16
00db10
+
00db10
+	/* At this point, we are necessarily 64-byte aligned.  If no zeroes were
00db10
+	   found, jump to the vectorized loop.  */
00db10
+	beq	cr7,L(preloop)
00db10
+
00db10
+L(dword_zero):
00db10
+	/* OK, one (or both) of the doublewords contains a null byte.  Check
00db10
+	   the first doubleword and decrement the address in case the first
00db10
+	   doubleword really contains a null byte.  */
00db10
+
00db10
+	cmpdi	cr6,r10,0
00db10
+	addi	r4,r4,-8
00db10
+	bne	cr6,L(done)
00db10
+
00db10
+	/* The null byte must be in the second doubleword.  Adjust the address
00db10
+	   again and move the result of cmpb to r10 so we can calculate the
00db10
+	   length.  */
00db10
+
00db10
+	mr	r10,r11
00db10
+	addi	r4,r4,8
00db10
+
00db10
+	/* If the null byte was found in the non-vectorized code, compute the
00db10
+	   final length.  r10 has the output of the cmpb instruction, that is,
00db10
+	   it contains 0xff in the same position as the null byte in the
00db10
+	   original doubleword from the string.  Use that to calculate the
00db10
+	   length.  */
00db10
+L(done):
00db10
+#ifdef __LITTLE_ENDIAN__
00db10
+	addi	r9, r10,-1    /* Form a mask from trailing zeros.  */
00db10
+	andc	r9, r9,r10
00db10
+	popcntd	r0, r9	      /* Count the bits in the mask.  */
00db10
+#else
00db10
+	cntlzd	r0,r10	      /* Count leading zeros before the match.  */
00db10
+#endif
00db10
+	subf	r5,r3,r4
00db10
+	srdi	r0,r0,3	      /* Convert leading/trailing zeros to bytes.  */
00db10
+	add	r3,r5,r0      /* Compute final length.  */
00db10
+	blr
00db10
+
00db10
+	/* Vectorized implementation starts here.  */
00db10
+	.p2align  4
00db10
+L(preloop):
00db10
+	/* Set up for the loop.  */
00db10
+	mr	r4,r9
00db10
+	li	r7, 16	      /* Load required offsets.  */
00db10
+	li	r8, 32
00db10
+	li	r9, 48
00db10
+	li	r12, 8
00db10
+	vxor	v0,v0,v0      /* VR with null chars to use with
00db10
+				 vcmpequb.  */
00db10
+
00db10
+	/* Main loop to look for the end of the string.  We will read in
00db10
+	   64-byte chunks.  Align it to 32 bytes and unroll it 3 times to
00db10
+	   leverage the icache performance.  */
00db10
+	.p2align  5
00db10
+L(loop):
00db10
+	lvx	  v1,r4,r0  /* Load 4 quadwords.  */
00db10
+	lvx	  v2,r4,r7
00db10
+	lvx	  v3,r4,r8
00db10
+	lvx	  v4,r4,r9
00db10
+	vminub	  v5,v1,v2  /* Compare and merge into one VR for speed.  */
00db10
+	vminub	  v6,v3,v4
00db10
+	vminub	  v7,v5,v6
00db10
+	vcmpequb. v7,v7,v0  /* Check for NULLs.  */
00db10
+	addi	  r4,r4,64  /* Adjust address for the next iteration.  */
00db10
+	bne	  cr6,L(vmx_zero)
00db10
+
00db10
+	lvx	  v1,r4,r0  /* Load 4 quadwords.  */
00db10
+	lvx	  v2,r4,r7
00db10
+	lvx	  v3,r4,r8
00db10
+	lvx	  v4,r4,r9
00db10
+	vminub	  v5,v1,v2  /* Compare and merge into one VR for speed.  */
00db10
+	vminub	  v6,v3,v4
00db10
+	vminub	  v7,v5,v6
00db10
+	vcmpequb. v7,v7,v0  /* Check for NULLs.  */
00db10
+	addi	  r4,r4,64  /* Adjust address for the next iteration.  */
00db10
+	bne	  cr6,L(vmx_zero)
00db10
+
00db10
+	lvx	  v1,r4,r0  /* Load 4 quadwords.  */
00db10
+	lvx	  v2,r4,r7
00db10
+	lvx	  v3,r4,r8
00db10
+	lvx	  v4,r4,r9
00db10
+	vminub	  v5,v1,v2  /* Compare and merge into one VR for speed.  */
00db10
+	vminub	  v6,v3,v4
00db10
+	vminub	  v7,v5,v6
00db10
+	vcmpequb. v7,v7,v0  /* Check for NULLs.  */
00db10
+	addi	  r4,r4,64  /* Adjust address for the next iteration.  */
00db10
+	beq	  cr6,L(loop)
00db10
+
00db10
+L(vmx_zero):
00db10
+	/* OK, we found a null byte.  Let's look for it in the current 64-byte
00db10
+	   block and mark it in its corresponding VR.  */
00db10
+	vcmpequb  v1,v1,v0
00db10
+	vcmpequb  v2,v2,v0
00db10
+	vcmpequb  v3,v3,v0
00db10
+	vcmpequb  v4,v4,v0
00db10
+
00db10
+	/* We will now 'compress' the result into a single doubleword, so it
00db10
+	   can be moved to a GPR for the final calculation.  First, we
00db10
+	   generate an appropriate mask for vbpermq, so we can permute bits into
00db10
+	   the first halfword.  */
00db10
+	vspltisb  v10,3
00db10
+	lvsl	  v11,r0,r0
00db10
+	vslb	  v10,v11,v10
00db10
+
00db10
+	/* Permute the first bit of each byte into bits 48-63.  */
00db10
+	VBPERMQ(v1,v1,v10)
00db10
+	VBPERMQ(v2,v2,v10)
00db10
+	VBPERMQ(v3,v3,v10)
00db10
+	VBPERMQ(v4,v4,v10)
00db10
+
00db10
+	/* Shift each component into its correct position for merging.  */
00db10
+#ifdef __LITTLE_ENDIAN__
00db10
+	vsldoi  v2,v2,v2,2
00db10
+	vsldoi  v3,v3,v3,4
00db10
+	vsldoi  v4,v4,v4,6
00db10
+#else
00db10
+	vsldoi	v1,v1,v1,6
00db10
+	vsldoi	v2,v2,v2,4
00db10
+	vsldoi	v3,v3,v3,2
00db10
+#endif
00db10
+
00db10
+	/* Merge the results and move to a GPR.  */
00db10
+	vor	v1,v2,v1
00db10
+	vor	v2,v3,v4
00db10
+	vor	v4,v1,v2
00db10
+	MFVRD(r10,v4)
00db10
+
00db10
+	 /* Adjust address to the begninning of the current 64-byte block.  */
00db10
+	addi	r4,r4,-64
00db10
+
00db10
+#ifdef __LITTLE_ENDIAN__
00db10
+	addi	r9, r10,-1    /* Form a mask from trailing zeros.  */
00db10
+	andc	r9, r9,r10
00db10
+	popcntd	r0, r9	      /* Count the bits in the mask.  */
00db10
+#else
00db10
+	cntlzd	r0,r10	      /* Count leading zeros before the match.  */
00db10
+#endif
00db10
+	subf	r5,r3,r4
00db10
+	add	r3,r5,r0      /* Compute final length.  */
00db10
+	blr
00db10
+
00db10
+END (strlen)
00db10
+libc_hidden_builtin_def (strlen)
00db10
-- 
00db10
2.1.0
00db10