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